Repository: damian-pastorini/reldens
Branch: master
Commit: e4a1d2f5d78a
Files: 991
Total size: 5.4 MB
Directory structure:
gitextract__red38cn/
├── .claude/
│ ├── client-camera-follow-system.md
│ ├── commands-reference.md
│ ├── entities-reference.md
│ ├── environment-variables.md
│ ├── feature-modules.md
│ ├── guest-system-technical-guide.md
│ ├── installer-guide.md
│ ├── items-system-implementation.md
│ ├── player-state-flow.md
│ ├── room-data-optimization.md
│ ├── room-images-tileset-override-flow.md
│ ├── stat-bars-configuration.md
│ ├── storage-architecture.md
│ ├── trade-system-implementation.md
│ └── ui-visibility-configuration.md
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── codeql.yml
├── .gitignore
├── CLAUDE.md
├── LICENSE
├── README.md
├── bin/
│ ├── commander.js
│ ├── generate.js
│ ├── import.js
│ ├── install-test.js
│ └── reldens-commands.js
├── client.js
├── generated-entities/
│ ├── entities/
│ │ ├── ads-banner-entity.js
│ │ ├── ads-entity.js
│ │ ├── ads-event-video-entity.js
│ │ ├── ads-played-entity.js
│ │ ├── ads-providers-entity.js
│ │ ├── ads-types-entity.js
│ │ ├── audio-categories-entity.js
│ │ ├── audio-entity.js
│ │ ├── audio-markers-entity.js
│ │ ├── audio-player-config-entity.js
│ │ ├── chat-entity.js
│ │ ├── chat-message-types-entity.js
│ │ ├── clan-entity.js
│ │ ├── clan-levels-entity.js
│ │ ├── clan-levels-modifiers-entity.js
│ │ ├── clan-members-entity.js
│ │ ├── config-entity.js
│ │ ├── config-types-entity.js
│ │ ├── drops-animations-entity.js
│ │ ├── features-entity.js
│ │ ├── items-group-entity.js
│ │ ├── items-inventory-entity.js
│ │ ├── items-item-entity.js
│ │ ├── items-item-modifiers-entity.js
│ │ ├── items-types-entity.js
│ │ ├── locale-entity.js
│ │ ├── objects-animations-entity.js
│ │ ├── objects-assets-entity.js
│ │ ├── objects-entity.js
│ │ ├── objects-items-inventory-entity.js
│ │ ├── objects-items-requirements-entity.js
│ │ ├── objects-items-rewards-entity.js
│ │ ├── objects-skills-entity.js
│ │ ├── objects-stats-entity.js
│ │ ├── objects-types-entity.js
│ │ ├── operation-types-entity.js
│ │ ├── players-entity.js
│ │ ├── players-state-entity.js
│ │ ├── players-stats-entity.js
│ │ ├── respawn-entity.js
│ │ ├── rewards-entity.js
│ │ ├── rewards-events-entity.js
│ │ ├── rewards-events-state-entity.js
│ │ ├── rewards-modifiers-entity.js
│ │ ├── rooms-change-points-entity.js
│ │ ├── rooms-entity.js
│ │ ├── rooms-return-points-entity.js
│ │ ├── scores-detail-entity.js
│ │ ├── scores-entity.js
│ │ ├── skills-class-level-up-animations-entity.js
│ │ ├── skills-class-path-entity.js
│ │ ├── skills-class-path-level-labels-entity.js
│ │ ├── skills-class-path-level-skills-entity.js
│ │ ├── skills-groups-entity.js
│ │ ├── skills-levels-entity.js
│ │ ├── skills-levels-modifiers-conditions-entity.js
│ │ ├── skills-levels-modifiers-entity.js
│ │ ├── skills-levels-set-entity.js
│ │ ├── skills-owners-class-path-entity.js
│ │ ├── skills-skill-animations-entity.js
│ │ ├── skills-skill-attack-entity.js
│ │ ├── skills-skill-entity.js
│ │ ├── skills-skill-group-relation-entity.js
│ │ ├── skills-skill-owner-conditions-entity.js
│ │ ├── skills-skill-owner-effects-conditions-entity.js
│ │ ├── skills-skill-owner-effects-entity.js
│ │ ├── skills-skill-physical-data-entity.js
│ │ ├── skills-skill-target-effects-conditions-entity.js
│ │ ├── skills-skill-target-effects-entity.js
│ │ ├── skills-skill-type-entity.js
│ │ ├── snippets-entity.js
│ │ ├── stats-entity.js
│ │ ├── target-options-entity.js
│ │ ├── users-entity.js
│ │ ├── users-locale-entity.js
│ │ └── users-login-entity.js
│ ├── entities-config.js
│ ├── entities-translations.js
│ └── models/
│ ├── mikro-orm/
│ │ ├── ads-banner-model.js
│ │ ├── ads-event-video-model.js
│ │ ├── ads-model.js
│ │ ├── ads-played-model.js
│ │ ├── ads-providers-model.js
│ │ ├── ads-types-model.js
│ │ ├── audio-categories-model.js
│ │ ├── audio-markers-model.js
│ │ ├── audio-model.js
│ │ ├── audio-player-config-model.js
│ │ ├── chat-message-types-model.js
│ │ ├── chat-model.js
│ │ ├── clan-levels-model.js
│ │ ├── clan-levels-modifiers-model.js
│ │ ├── clan-members-model.js
│ │ ├── clan-model.js
│ │ ├── config-model.js
│ │ ├── config-types-model.js
│ │ ├── drops-animations-model.js
│ │ ├── features-model.js
│ │ ├── items-group-model.js
│ │ ├── items-inventory-model.js
│ │ ├── items-item-model.js
│ │ ├── items-item-modifiers-model.js
│ │ ├── items-types-model.js
│ │ ├── locale-model.js
│ │ ├── objects-animations-model.js
│ │ ├── objects-assets-model.js
│ │ ├── objects-items-inventory-model.js
│ │ ├── objects-items-requirements-model.js
│ │ ├── objects-items-rewards-model.js
│ │ ├── objects-model.js
│ │ ├── objects-skills-model.js
│ │ ├── objects-stats-model.js
│ │ ├── objects-types-model.js
│ │ ├── operation-types-model.js
│ │ ├── players-model.js
│ │ ├── players-state-model.js
│ │ ├── players-stats-model.js
│ │ ├── registered-models-mikro-orm.js
│ │ ├── respawn-model.js
│ │ ├── rewards-events-model.js
│ │ ├── rewards-events-state-model.js
│ │ ├── rewards-model.js
│ │ ├── rewards-modifiers-model.js
│ │ ├── rooms-change-points-model.js
│ │ ├── rooms-model.js
│ │ ├── rooms-return-points-model.js
│ │ ├── scores-detail-model.js
│ │ ├── scores-model.js
│ │ ├── skills-class-level-up-animations-model.js
│ │ ├── skills-class-path-level-labels-model.js
│ │ ├── skills-class-path-level-skills-model.js
│ │ ├── skills-class-path-model.js
│ │ ├── skills-groups-model.js
│ │ ├── skills-levels-model.js
│ │ ├── skills-levels-modifiers-conditions-model.js
│ │ ├── skills-levels-modifiers-model.js
│ │ ├── skills-levels-set-model.js
│ │ ├── skills-owners-class-path-model.js
│ │ ├── skills-skill-animations-model.js
│ │ ├── skills-skill-attack-model.js
│ │ ├── skills-skill-group-relation-model.js
│ │ ├── skills-skill-model.js
│ │ ├── skills-skill-owner-conditions-model.js
│ │ ├── skills-skill-owner-effects-conditions-model.js
│ │ ├── skills-skill-owner-effects-model.js
│ │ ├── skills-skill-physical-data-model.js
│ │ ├── skills-skill-target-effects-conditions-model.js
│ │ ├── skills-skill-target-effects-model.js
│ │ ├── skills-skill-type-model.js
│ │ ├── snippets-model.js
│ │ ├── stats-model.js
│ │ ├── target-options-model.js
│ │ ├── users-locale-model.js
│ │ ├── users-login-model.js
│ │ └── users-model.js
│ ├── objection-js/
│ │ ├── ads-banner-model.js
│ │ ├── ads-event-video-model.js
│ │ ├── ads-model.js
│ │ ├── ads-played-model.js
│ │ ├── ads-providers-model.js
│ │ ├── ads-types-model.js
│ │ ├── audio-categories-model.js
│ │ ├── audio-markers-model.js
│ │ ├── audio-model.js
│ │ ├── audio-player-config-model.js
│ │ ├── chat-message-types-model.js
│ │ ├── chat-model.js
│ │ ├── clan-levels-model.js
│ │ ├── clan-levels-modifiers-model.js
│ │ ├── clan-members-model.js
│ │ ├── clan-model.js
│ │ ├── config-model.js
│ │ ├── config-types-model.js
│ │ ├── drops-animations-model.js
│ │ ├── features-model.js
│ │ ├── items-group-model.js
│ │ ├── items-inventory-model.js
│ │ ├── items-item-model.js
│ │ ├── items-item-modifiers-model.js
│ │ ├── items-types-model.js
│ │ ├── locale-model.js
│ │ ├── objects-animations-model.js
│ │ ├── objects-assets-model.js
│ │ ├── objects-items-inventory-model.js
│ │ ├── objects-items-requirements-model.js
│ │ ├── objects-items-rewards-model.js
│ │ ├── objects-model.js
│ │ ├── objects-skills-model.js
│ │ ├── objects-stats-model.js
│ │ ├── objects-types-model.js
│ │ ├── operation-types-model.js
│ │ ├── players-model.js
│ │ ├── players-state-model.js
│ │ ├── players-stats-model.js
│ │ ├── registered-models-objection-js.js
│ │ ├── respawn-model.js
│ │ ├── rewards-events-model.js
│ │ ├── rewards-events-state-model.js
│ │ ├── rewards-model.js
│ │ ├── rewards-modifiers-model.js
│ │ ├── rooms-change-points-model.js
│ │ ├── rooms-model.js
│ │ ├── rooms-return-points-model.js
│ │ ├── scores-detail-model.js
│ │ ├── scores-model.js
│ │ ├── skills-class-level-up-animations-model.js
│ │ ├── skills-class-path-level-labels-model.js
│ │ ├── skills-class-path-level-skills-model.js
│ │ ├── skills-class-path-model.js
│ │ ├── skills-groups-model.js
│ │ ├── skills-levels-model.js
│ │ ├── skills-levels-modifiers-conditions-model.js
│ │ ├── skills-levels-modifiers-model.js
│ │ ├── skills-levels-set-model.js
│ │ ├── skills-owners-class-path-model.js
│ │ ├── skills-skill-animations-model.js
│ │ ├── skills-skill-attack-model.js
│ │ ├── skills-skill-group-relation-model.js
│ │ ├── skills-skill-model.js
│ │ ├── skills-skill-owner-conditions-model.js
│ │ ├── skills-skill-owner-effects-conditions-model.js
│ │ ├── skills-skill-owner-effects-model.js
│ │ ├── skills-skill-physical-data-model.js
│ │ ├── skills-skill-target-effects-conditions-model.js
│ │ ├── skills-skill-target-effects-model.js
│ │ ├── skills-skill-type-model.js
│ │ ├── snippets-model.js
│ │ ├── stats-model.js
│ │ ├── target-options-model.js
│ │ ├── users-locale-model.js
│ │ ├── users-login-model.js
│ │ └── users-model.js
│ └── prisma/
│ ├── ads-banner-model.js
│ ├── ads-event-video-model.js
│ ├── ads-model.js
│ ├── ads-played-model.js
│ ├── ads-providers-model.js
│ ├── ads-types-model.js
│ ├── audio-categories-model.js
│ ├── audio-markers-model.js
│ ├── audio-model.js
│ ├── audio-player-config-model.js
│ ├── chat-message-types-model.js
│ ├── chat-model.js
│ ├── clan-levels-model.js
│ ├── clan-levels-modifiers-model.js
│ ├── clan-members-model.js
│ ├── clan-model.js
│ ├── config-model.js
│ ├── config-types-model.js
│ ├── drops-animations-model.js
│ ├── features-model.js
│ ├── items-group-model.js
│ ├── items-inventory-model.js
│ ├── items-item-model.js
│ ├── items-item-modifiers-model.js
│ ├── items-types-model.js
│ ├── locale-model.js
│ ├── objects-animations-model.js
│ ├── objects-assets-model.js
│ ├── objects-items-inventory-model.js
│ ├── objects-items-requirements-model.js
│ ├── objects-items-rewards-model.js
│ ├── objects-model.js
│ ├── objects-skills-model.js
│ ├── objects-stats-model.js
│ ├── objects-types-model.js
│ ├── operation-types-model.js
│ ├── players-model.js
│ ├── players-state-model.js
│ ├── players-stats-model.js
│ ├── registered-models-prisma.js
│ ├── respawn-model.js
│ ├── rewards-events-model.js
│ ├── rewards-events-state-model.js
│ ├── rewards-model.js
│ ├── rewards-modifiers-model.js
│ ├── rooms-change-points-model.js
│ ├── rooms-model.js
│ ├── rooms-return-points-model.js
│ ├── scores-detail-model.js
│ ├── scores-model.js
│ ├── skills-class-level-up-animations-model.js
│ ├── skills-class-path-level-labels-model.js
│ ├── skills-class-path-level-skills-model.js
│ ├── skills-class-path-model.js
│ ├── skills-groups-model.js
│ ├── skills-levels-model.js
│ ├── skills-levels-modifiers-conditions-model.js
│ ├── skills-levels-modifiers-model.js
│ ├── skills-levels-set-model.js
│ ├── skills-owners-class-path-model.js
│ ├── skills-skill-animations-model.js
│ ├── skills-skill-attack-model.js
│ ├── skills-skill-group-relation-model.js
│ ├── skills-skill-model.js
│ ├── skills-skill-owner-conditions-model.js
│ ├── skills-skill-owner-effects-conditions-model.js
│ ├── skills-skill-owner-effects-model.js
│ ├── skills-skill-physical-data-model.js
│ ├── skills-skill-target-effects-conditions-model.js
│ ├── skills-skill-target-effects-model.js
│ ├── skills-skill-type-model.js
│ ├── snippets-model.js
│ ├── stats-model.js
│ ├── target-options-model.js
│ ├── users-locale-model.js
│ ├── users-login-model.js
│ └── users-model.js
├── install/
│ ├── css/
│ │ └── styles.scss
│ ├── index.html
│ ├── index.js
│ └── site.webmanifest
├── lib/
│ ├── actions/
│ │ ├── client/
│ │ │ ├── game-manager-enricher.js
│ │ │ ├── messages-guard.js
│ │ │ ├── messages-handler.js
│ │ │ ├── player-selector.js
│ │ │ ├── plugin.js
│ │ │ ├── preloader-handler.js
│ │ │ ├── receiver-wrapper.js
│ │ │ ├── skills-ui.js
│ │ │ └── snippets/
│ │ │ └── en_US.js
│ │ ├── constants.js
│ │ ├── factories/
│ │ │ ├── class-path-key-factory.js
│ │ │ └── skill-data-factory.js
│ │ ├── schemas/
│ │ │ └── skill-schema.js
│ │ └── server/
│ │ ├── battle-end-action.js
│ │ ├── battle.js
│ │ ├── data-loader.js
│ │ ├── entities/
│ │ │ ├── operation-types-entity-override.js
│ │ │ ├── skills-class-path-entity-override.js
│ │ │ ├── skills-levels-modifiers-entity-override.js
│ │ │ ├── skills-levels-set-entity-override.js
│ │ │ ├── skills-owners-class-path-entity-override.js
│ │ │ ├── skills-skill-animations-entity-override.js
│ │ │ ├── skills-skill-attack-entity-override.js
│ │ │ ├── skills-skill-entity-override.js
│ │ │ ├── skills-skill-owner-effects-entity-override.js
│ │ │ └── skills-skill-target-effects-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── event-listeners.js
│ │ ├── events/
│ │ │ ├── battle-ended-event.js
│ │ │ └── player-death-event.js
│ │ ├── initial-game-data-enricher.js
│ │ ├── message-actions.js
│ │ ├── models-manager.js
│ │ ├── player-class-path-handler.js
│ │ ├── player-enricher.js
│ │ ├── plugin.js
│ │ ├── pve.js
│ │ ├── pvp.js
│ │ ├── skills/
│ │ │ ├── type-attack.js
│ │ │ ├── type-effect.js
│ │ │ ├── type-physical-attack.js
│ │ │ ├── type-physical-effect.js
│ │ │ └── types.js
│ │ ├── skills-class-path-loader.js
│ │ ├── skills-extra-data-mapper.js
│ │ ├── storage/
│ │ │ ├── class-path-generator.js
│ │ │ ├── conditions-generator.js
│ │ │ ├── levels-generator.js
│ │ │ ├── modifiers-generator.js
│ │ │ └── skills-generator.js
│ │ └── storage-observer.js
│ ├── admin/
│ │ └── server/
│ │ ├── entities-config-override.js
│ │ ├── plugin.js
│ │ ├── room-map-tilesets-validator.js
│ │ ├── rooms-file-upload-renderer.js
│ │ ├── subscribers/
│ │ │ ├── create-admin-subscriber.js
│ │ │ ├── generators-routes-subscriber.js
│ │ │ ├── maps-wizard-subscriber.js
│ │ │ ├── objects-importer-subscriber.js
│ │ │ ├── rooms-entity-subscriber.js
│ │ │ ├── shutdown-subscriber.js
│ │ │ ├── skills-importer-subscriber.js
│ │ │ └── theme-manager-subscriber.js
│ │ └── templates-list.js
│ ├── ads/
│ │ ├── client/
│ │ │ ├── ads-provider.js
│ │ │ ├── messages-listener.js
│ │ │ ├── plugin.js
│ │ │ ├── providers/
│ │ │ │ ├── crazy-games/
│ │ │ │ │ ├── banners-handler.js
│ │ │ │ │ ├── validator.js
│ │ │ │ │ └── videos-handler.js
│ │ │ │ ├── crazy-games.js
│ │ │ │ ├── game-monetize.js
│ │ │ │ └── google-ad-sense.js
│ │ │ ├── providers-list.js
│ │ │ ├── sdk-handler.js
│ │ │ └── snippets/
│ │ │ └── en_US.js
│ │ ├── constants.js
│ │ └── server/
│ │ ├── ads-start-handler.js
│ │ ├── ads-type/
│ │ │ ├── banner.js
│ │ │ ├── base-ad.js
│ │ │ └── event-video.js
│ │ ├── entities/
│ │ │ └── ads-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── event-handlers/
│ │ │ └── create-player-ads-handler.js
│ │ ├── message-actions.js
│ │ └── plugin.js
│ ├── audio/
│ │ ├── client/
│ │ │ ├── audio-ui.js
│ │ │ ├── audio-update.js
│ │ │ ├── manager.js
│ │ │ ├── messages-listener.js
│ │ │ ├── plugin.js
│ │ │ ├── scene-audio-player.js
│ │ │ └── snippets/
│ │ │ └── en_US.js
│ │ ├── constants.js
│ │ └── server/
│ │ ├── audio-hot-plug-callbacks.js
│ │ ├── entities/
│ │ │ └── audio-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── manager.js
│ │ └── plugin.js
│ ├── bundlers/
│ │ └── drivers/
│ │ └── parcel-config.json
│ ├── chat/
│ │ ├── cleaner.js
│ │ ├── client/
│ │ │ ├── chat-tabs.js
│ │ │ ├── chat-ui.js
│ │ │ ├── messages-listener.js
│ │ │ ├── plugin.js
│ │ │ ├── snippets/
│ │ │ │ └── en_US.js
│ │ │ └── templates-handler.js
│ │ ├── constants.js
│ │ ├── message-factory.js
│ │ └── server/
│ │ ├── entities/
│ │ │ ├── chat-entity-override.js
│ │ │ └── chat-message-types-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── event-listener/
│ │ │ ├── guest-invalid-change-point.js
│ │ │ ├── npc-skills.js
│ │ │ └── player-skills.js
│ │ ├── manager.js
│ │ ├── message-actions.js
│ │ ├── messages/
│ │ │ ├── message-data-mapper.js
│ │ │ ├── npc-damage-callback.js
│ │ │ ├── npc-dodge-callback.js
│ │ │ ├── npc-modifiers-callback.js
│ │ │ ├── player-damage-callback.js
│ │ │ ├── player-dodge-callback.js
│ │ │ ├── player-modifiers-callback.js
│ │ │ └── validator.js
│ │ ├── messages-guard.js
│ │ ├── plugin.js
│ │ └── room-chat.js
│ ├── config/
│ │ ├── client/
│ │ │ └── config-manager.js
│ │ ├── constants.js
│ │ ├── processor.js
│ │ └── server/
│ │ ├── entities/
│ │ │ ├── config-entity-override.js
│ │ │ └── config-types-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ └── manager.js
│ ├── features/
│ │ ├── client/
│ │ │ ├── config-client.js
│ │ │ └── manager.js
│ │ ├── plugin-interface.js
│ │ └── server/
│ │ ├── config-server.js
│ │ ├── entities/
│ │ │ ├── features-entity-override.js
│ │ │ └── features-entity.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── manager.js
│ │ └── setup-server-properties.js
│ ├── firebase/
│ │ ├── client/
│ │ │ └── connector.js
│ │ └── server/
│ │ └── plugin.js
│ ├── game/
│ │ ├── allowed-file-types.js
│ │ ├── client/
│ │ │ ├── animations-defaults-merger.js
│ │ │ ├── communication/
│ │ │ │ ├── room-state-entities-manager.js
│ │ │ │ └── state-callbacks-manager.js
│ │ │ ├── engine/
│ │ │ │ └── sprite-text-factory.js
│ │ │ ├── fps-counter.js
│ │ │ ├── game-client.js
│ │ │ ├── game-dom.js
│ │ │ ├── game-engine.js
│ │ │ ├── game-manager.js
│ │ │ ├── handlers/
│ │ │ │ ├── client-start-handler.js
│ │ │ │ ├── errors-block-handler.js
│ │ │ │ ├── forgot-password-form-handler.js
│ │ │ │ ├── full-screen-handler.js
│ │ │ │ ├── guest-form-handler.js
│ │ │ │ ├── login-form-handler.js
│ │ │ │ ├── registration-form-handler.js
│ │ │ │ └── terms-and-conditions-handler.js
│ │ │ ├── instructions-ui.js
│ │ │ ├── joystick.js
│ │ │ ├── minimap-ui.js
│ │ │ ├── minimap.js
│ │ │ ├── room-events.js
│ │ │ ├── scene-dynamic.js
│ │ │ ├── scene-preloader.js
│ │ │ ├── settings-ui.js
│ │ │ ├── snippets/
│ │ │ │ └── en_US.js
│ │ │ ├── tileset-animation.js
│ │ │ ├── ui-factory.js
│ │ │ └── user-interface.js
│ │ ├── constants.js
│ │ ├── mime-types.js
│ │ ├── properties-handler.js
│ │ ├── reldens-ascii.js
│ │ ├── server/
│ │ │ ├── client-wrapper.js
│ │ │ ├── data-server-config.js
│ │ │ ├── data-server-initializer.js
│ │ │ ├── entities-loader.js
│ │ │ ├── entity-properties.js
│ │ │ ├── forgot-password.js
│ │ │ ├── game-server.js
│ │ │ ├── homepage-loader.js
│ │ │ ├── install-templates/
│ │ │ │ ├── .gitignore.dist
│ │ │ │ ├── data-package.json
│ │ │ │ └── knexfile.js.dist
│ │ │ ├── installer/
│ │ │ │ ├── entities-installation.js
│ │ │ │ ├── generic-driver-installation.js
│ │ │ │ ├── packages-installation.js
│ │ │ │ ├── prisma-installation.js
│ │ │ │ ├── prisma-subprocess-worker.js
│ │ │ │ └── project-files-creation.js
│ │ │ ├── installer.js
│ │ │ ├── login-manager.js
│ │ │ ├── mailer/
│ │ │ │ ├── nodemailer-factory.js
│ │ │ │ └── sendgrid-factory.js
│ │ │ ├── mailer.js
│ │ │ ├── manager.js
│ │ │ ├── maps-loader.js
│ │ │ ├── memory/
│ │ │ │ ├── active-player.js
│ │ │ │ └── active-players.js
│ │ │ ├── storage/
│ │ │ │ └── drivers-map.js
│ │ │ ├── template-engine.js
│ │ │ ├── templates-to-path-mapper.js
│ │ │ └── theme-manager.js
│ │ └── type-determiner.js
│ ├── import/
│ │ └── server/
│ │ ├── attributes-per-level-importer.js
│ │ ├── class-paths-importer.js
│ │ ├── maps-importer.js
│ │ ├── objects-importer.js
│ │ ├── players-experience-per-level-importer.js
│ │ ├── skills-importer.js
│ │ └── tile-extruder.js
│ ├── inventory/
│ │ ├── client/
│ │ │ ├── exchange/
│ │ │ │ └── trade-target-action.js
│ │ │ ├── inventory-receiver.js
│ │ │ ├── inventory-ui.js
│ │ │ ├── plugin.js
│ │ │ ├── snippets/
│ │ │ │ └── en_US.js
│ │ │ ├── templates-handler.js
│ │ │ ├── trade-items-helper.js
│ │ │ ├── trade-message-handler.js
│ │ │ └── trade-message-listener.js
│ │ ├── constants.js
│ │ └── server/
│ │ ├── entities/
│ │ │ ├── items-group-entity-override.js
│ │ │ ├── items-inventory-entity-override.js
│ │ │ └── items-item-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── exchange/
│ │ │ ├── player-processor.js
│ │ │ └── processor.js
│ │ ├── group-hot-plug-callbacks.js
│ │ ├── groups-data-remover.js
│ │ ├── items-factory.js
│ │ ├── message-actions.js
│ │ ├── models-manager.js
│ │ ├── plugin.js
│ │ ├── storage-observer.js
│ │ └── subscribers/
│ │ ├── player-death-subscriber.js
│ │ ├── player-subscriber.js
│ │ └── server-subscriber.js
│ ├── objects/
│ │ ├── client/
│ │ │ ├── animation-engine.js
│ │ │ ├── drops-message-listener.js
│ │ │ ├── objects-message-listener.js
│ │ │ ├── plugin.js
│ │ │ ├── snippets/
│ │ │ │ └── en_US.js
│ │ │ └── trader-object-ui.js
│ │ ├── constants.js
│ │ └── server/
│ │ ├── entities/
│ │ │ └── objects-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── handler/
│ │ │ └── objects-class-type.js
│ │ ├── manager.js
│ │ ├── object/
│ │ │ ├── object-types-classes.js
│ │ │ ├── object-types.js
│ │ │ └── type/
│ │ │ ├── animation-object.js
│ │ │ ├── base-object.js
│ │ │ ├── drop-object.js
│ │ │ ├── enemy-object.js
│ │ │ ├── multiple-object.js
│ │ │ ├── npc-object.js
│ │ │ └── trader-object.js
│ │ └── plugin.js
│ ├── prediction/
│ │ └── client/
│ │ ├── player-engine-prediction.js
│ │ ├── plugin.js
│ │ ├── prediction-world-creator.js
│ │ └── room-events-override.js
│ ├── respawn/
│ │ └── server/
│ │ ├── entities/
│ │ │ └── respawn-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── plugin.js
│ │ └── room-respawn.js
│ ├── rewards/
│ │ ├── client/
│ │ │ ├── message-handler.js
│ │ │ ├── message-listener.js
│ │ │ ├── messages-processor.js
│ │ │ ├── plugin.js
│ │ │ ├── preloader-handler.js
│ │ │ └── snippets/
│ │ │ └── en_US.js
│ │ ├── constants.js
│ │ └── server/
│ │ ├── actions/
│ │ │ └── give-reward-action.js
│ │ ├── add-item-to-inventory.js
│ │ ├── drops-animations.js
│ │ ├── entities/
│ │ │ ├── drops-animations-entity-override.js
│ │ │ ├── rewards-entity-override.js
│ │ │ ├── rewards-events-entity-override.js
│ │ │ └── rewards-modifiers-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── event-handlers/
│ │ │ ├── event-handler.js
│ │ │ └── login-state-handler.js
│ │ ├── mappers/
│ │ │ ├── rewards-events-mapper.js
│ │ │ └── rewards-to-actions-mapper.js
│ │ ├── pick-up-object.js
│ │ ├── plugin.js
│ │ ├── repositories-extension.js
│ │ ├── reward-message-actions.js
│ │ ├── reward.js
│ │ ├── rewards-drops-mapper.js
│ │ ├── rewards-drops-processor.js
│ │ ├── rewards-events-data-sender.js
│ │ ├── rewards-events-handler.js
│ │ ├── rewards-events-message-actions.js
│ │ ├── rewards-events-provider.js
│ │ ├── rewards-events-updater.js
│ │ ├── rewards-mapper.js
│ │ ├── subscribers/
│ │ │ ├── object-subscriber.js
│ │ │ └── rewards-subscriber.js
│ │ ├── target-determiner.js
│ │ ├── validator/
│ │ │ └── reward-drop-validator.js
│ │ └── world-drop-handler.js
│ ├── rooms/
│ │ ├── client/
│ │ │ └── plugin.js
│ │ ├── constants.js
│ │ └── server/
│ │ ├── entities/
│ │ │ └── rooms-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── events/
│ │ │ └── joined-scene-room-event.js
│ │ ├── game.js
│ │ ├── login.js
│ │ ├── manager.js
│ │ ├── plugin.js
│ │ ├── random-player-state.js
│ │ ├── scene-data-filter.js
│ │ ├── scene.js
│ │ ├── state.js
│ │ └── world-config.js
│ ├── scores/
│ │ ├── client/
│ │ │ ├── messages-processor.js
│ │ │ ├── plugin.js
│ │ │ ├── preloader-handler.js
│ │ │ ├── scores-message-handler.js
│ │ │ ├── scores-message-listener.js
│ │ │ └── snippets/
│ │ │ └── en_US.js
│ │ ├── constants.js
│ │ └── server/
│ │ ├── entities/
│ │ │ ├── scores-detail-entity-override.js
│ │ │ └── scores-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── mapper/
│ │ │ └── increase-score-on-kill-mapper.js
│ │ ├── plugin.js
│ │ ├── repositories-extension.js
│ │ ├── scores-provider.js
│ │ ├── scores-sender.js
│ │ ├── scores-updater.js
│ │ └── subscriber/
│ │ ├── create-scores-route.js
│ │ ├── increase-score-on-kill.js
│ │ └── send-initial-scores-data.js
│ ├── snippets/
│ │ ├── client/
│ │ │ ├── plugin.js
│ │ │ ├── snippets/
│ │ │ │ └── en_US.js
│ │ │ ├── snippets-ui.js
│ │ │ ├── templates-handler.js
│ │ │ └── translations-mapper.js
│ │ ├── constants.js
│ │ ├── server/
│ │ │ ├── configuration-enricher.js
│ │ │ ├── entities/
│ │ │ │ ├── locale-entity-override.js
│ │ │ │ ├── snippets-entity-override.js
│ │ │ │ └── users-locale-entity-override.js
│ │ │ ├── entities-config.js
│ │ │ ├── entities-translations.js
│ │ │ ├── initial-game-data-enricher.js
│ │ │ └── plugin.js
│ │ └── translator.js
│ ├── teams/
│ │ ├── client/
│ │ │ ├── clan-message-handler.js
│ │ │ ├── clan-message-listener.js
│ │ │ ├── messages-processor.js
│ │ │ ├── plugin.js
│ │ │ ├── target-box-enricher.js
│ │ │ ├── team-message-handler.js
│ │ │ ├── team-message-listener.js
│ │ │ └── templates-handler.js
│ │ ├── constants.js
│ │ └── server/
│ │ ├── clan-factory.js
│ │ ├── clan-message-actions.js
│ │ ├── clan-updates-handler.js
│ │ ├── clan.js
│ │ ├── entities/
│ │ │ ├── clan-entity-override.js
│ │ │ └── clan-levels-modifiers-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── event-handlers/
│ │ │ ├── create-player-clan-handler.js
│ │ │ ├── create-player-team-handler.js
│ │ │ ├── end-player-hit-change-point-team-handler.js
│ │ │ └── stats-update-handler.js
│ │ ├── message-actions/
│ │ │ ├── chat-message-actions.js
│ │ │ ├── clan-create.js
│ │ │ ├── clan-disconnect.js
│ │ │ ├── clan-join.js
│ │ │ ├── clan-leave.js
│ │ │ ├── team-join.js
│ │ │ ├── team-leave.js
│ │ │ ├── try-clan-invite.js
│ │ │ └── try-team-start.js
│ │ ├── players-data-mapper.js
│ │ ├── plugin.js
│ │ ├── team-message-actions.js
│ │ ├── team-updates-handler.js
│ │ └── team.js
│ ├── users/
│ │ ├── client/
│ │ │ ├── bar-properties.js
│ │ │ ├── lifebar-ui.js
│ │ │ ├── objects-handler.js
│ │ │ ├── player-engine.js
│ │ │ ├── player-stats-bars-ui.js
│ │ │ ├── player-stats-ui.js
│ │ │ ├── plugin.js
│ │ │ └── snippets/
│ │ │ └── en_US.js
│ │ ├── constants.js
│ │ └── server/
│ │ ├── create-admin.js
│ │ ├── entities/
│ │ │ ├── players-entity-override.js
│ │ │ ├── players-state-entity-override.js
│ │ │ ├── players-stats-entity-override.js
│ │ │ ├── stats-entity-override.js
│ │ │ ├── users-entity-override.js
│ │ │ └── users-login-entity-override.js
│ │ ├── entities-config.js
│ │ ├── entities-translations.js
│ │ ├── manager.js
│ │ ├── player.js
│ │ ├── plugin.js
│ │ └── reset-password.js
│ └── world/
│ ├── client/
│ │ ├── debug-world-creator.js
│ │ ├── plugin.js
│ │ └── renderer.js
│ ├── constants.js
│ ├── server/
│ │ ├── body-state.js
│ │ ├── collisions-manager.js
│ │ ├── object-body-state.js
│ │ ├── p2world.js
│ │ ├── path-finder.js
│ │ ├── physical-body.js
│ │ └── world-walkable-nodes-around-provider.js
│ ├── world-points-validator.js
│ ├── world-position-calculator.js
│ └── world-timer.js
├── migrations/
│ ├── development/
│ │ ├── 20190923183906_v4.0.0.js
│ │ ├── beta.09-sql-update.sql
│ │ ├── beta.12-sql-update.sql
│ │ ├── beta.15-sql-update.sql
│ │ ├── beta.16-sql-update.sql
│ │ ├── beta.16.5-sql-update.sql
│ │ ├── beta.17-sql-update.sql
│ │ ├── beta.18-sql-update.sql
│ │ ├── beta.18.1-sql-update.sql
│ │ ├── beta.19-sql-update.sql
│ │ ├── beta.20-sql-update.sql
│ │ ├── beta.21-sql-update.sql
│ │ ├── beta.22-sql-update.sql
│ │ ├── beta.23-sql-update.sql
│ │ ├── beta.24-sql-update.sql
│ │ ├── beta.24.1-sql-update.sql
│ │ ├── beta.25-sql-update.sql
│ │ ├── beta.26-sql-update.sql
│ │ ├── beta.27-sql-update.sql
│ │ ├── beta.28-sql-update.sql
│ │ ├── beta.30-sql-update.sql
│ │ ├── beta.31-sql-update.sql
│ │ ├── beta.34-sql-update.sql
│ │ ├── beta.35-sql-update.sql
│ │ ├── beta.36-sql-update.sql
│ │ ├── beta.38-sql-update.sql
│ │ ├── beta.38.3-sql-update.sql
│ │ ├── beta.39-sql-update.sql
│ │ ├── beta.39.7-sql-update.sql
│ │ ├── beta.39.8-sql-update.sql
│ │ └── reldens-test-sample-data-v4.0.0.sql
│ └── production/
│ ├── 20190923181825_v4.0.0.js
│ ├── mongo-db-install.js
│ ├── reldens-basic-config-v4.0.0.sql
│ ├── reldens-install-v4.0.0.sql
│ └── reldens-sample-data-v4.0.0.sql
├── package.json
├── server.js
├── tests/
│ ├── base-test.js
│ ├── database-reset-utility.js
│ ├── fixtures/
│ │ ├── crud-test-data.js
│ │ ├── entities-list.js
│ │ ├── features-test-data.js
│ │ ├── generate-complete-comparison.js
│ │ ├── generate-entities-fixtures.js
│ │ └── test-file.json
│ ├── manager.js
│ ├── run.js
│ ├── test-admin-auth.js
│ ├── test-admin-crud.js
│ ├── test-admin-features.js
│ └── utils.js
└── theme/
├── admin/
│ ├── functions.js
│ ├── reldens-admin-client.css
│ ├── reldens-admin-client.js
│ ├── reldens-functions.js
│ └── templates/
│ ├── cache-clean-button.html
│ ├── clear-all-cache-button.html
│ ├── dashboard.html
│ ├── default-copyright.html
│ ├── edit.html
│ ├── fields/
│ │ ├── edit/
│ │ │ ├── button.html
│ │ │ ├── checkbox.html
│ │ │ ├── file.html
│ │ │ ├── radio.html
│ │ │ ├── select.html
│ │ │ ├── text.html
│ │ │ ├── textarea.html
│ │ │ ├── tileset-alert-wrapper.html
│ │ │ └── tileset-file-item.html
│ │ └── view/
│ │ ├── audio.html
│ │ ├── audios.html
│ │ ├── boolean.html
│ │ ├── image.html
│ │ ├── images.html
│ │ ├── link.html
│ │ ├── links.html
│ │ ├── text.html
│ │ └── textarea.html
│ ├── layout.html
│ ├── list-content.html
│ ├── list.html
│ ├── login.html
│ ├── management.html
│ ├── maps-wizard-maps-selection.html
│ ├── maps-wizard.html
│ ├── objects-import.html
│ ├── pagination-link.html
│ ├── sections/
│ │ ├── editForm/
│ │ │ └── cms-pages.html
│ │ ├── view/
│ │ │ └── rooms.html
│ │ └── viewForm/
│ │ └── cms-pages.html
│ ├── sidebar-header.html
│ ├── sidebar-item.html
│ ├── sidebar.html
│ ├── skills-import.html
│ ├── theme-manager.html
│ └── view.html
├── default/
│ ├── assets/
│ │ ├── email/
│ │ │ ├── forgot.html
│ │ │ ├── reset-error.html
│ │ │ └── reset-success.html
│ │ ├── features/
│ │ │ ├── chat/
│ │ │ │ └── templates/
│ │ │ │ ├── message.html
│ │ │ │ ├── tab-content.html
│ │ │ │ ├── tab-label.html
│ │ │ │ ├── tabs-container.html
│ │ │ │ └── ui-chat.html
│ │ │ ├── inventory/
│ │ │ │ └── templates/
│ │ │ │ ├── equip.html
│ │ │ │ ├── group.html
│ │ │ │ ├── item.html
│ │ │ │ ├── trade-accept.html
│ │ │ │ ├── trade-action-remove.html
│ │ │ │ ├── trade-action.html
│ │ │ │ ├── trade-container.html
│ │ │ │ ├── trade-item-quantity.html
│ │ │ │ ├── trade-item.html
│ │ │ │ ├── trade-player-container.html
│ │ │ │ ├── trade-requirements.html
│ │ │ │ ├── trade-rewards.html
│ │ │ │ ├── trade-start.html
│ │ │ │ ├── ui-equipment.html
│ │ │ │ ├── ui-inventory.html
│ │ │ │ └── usable.html
│ │ │ ├── rewards/
│ │ │ │ └── templates/
│ │ │ │ ├── ui-rewards-list.html
│ │ │ │ └── ui-rewards.html
│ │ │ ├── scores/
│ │ │ │ └── templates/
│ │ │ │ ├── ui-scores-table.html
│ │ │ │ └── ui-scores.html
│ │ │ ├── skills/
│ │ │ │ └── templates/
│ │ │ │ ├── ui-class-path.html
│ │ │ │ ├── ui-experience.html
│ │ │ │ ├── ui-level.html
│ │ │ │ ├── ui-skill-box.html
│ │ │ │ └── ui-skills.html
│ │ │ ├── snippets/
│ │ │ │ └── templates/
│ │ │ │ └── ui-snippets.html
│ │ │ └── teams/
│ │ │ └── templates/
│ │ │ ├── clan-accept.html
│ │ │ ├── clan-container.html
│ │ │ ├── clan-create.html
│ │ │ ├── clan-invite.html
│ │ │ ├── clan-member-data.html
│ │ │ ├── clan-player-data.html
│ │ │ ├── clan-remove.html
│ │ │ ├── shared-property.html
│ │ │ ├── team-accept.html
│ │ │ ├── team-container.html
│ │ │ ├── team-invite.html
│ │ │ ├── team-player-data.html
│ │ │ ├── team-remove.html
│ │ │ ├── ui-clan.html
│ │ │ └── ui-teams.html
│ │ ├── html/
│ │ │ ├── dialog-box.html
│ │ │ ├── layout.html
│ │ │ ├── player-stat.html
│ │ │ ├── player-stats-bar.html
│ │ │ ├── ui-action-box.html
│ │ │ ├── ui-audio-category-row.html
│ │ │ ├── ui-audio.html
│ │ │ ├── ui-controls.html
│ │ │ ├── ui-full-screen-button.html
│ │ │ ├── ui-instructions.html
│ │ │ ├── ui-loading.html
│ │ │ ├── ui-minimap.html
│ │ │ ├── ui-option-button.html
│ │ │ ├── ui-option-icon.html
│ │ │ ├── ui-options-container.html
│ │ │ ├── ui-player-box.html
│ │ │ ├── ui-player-stats.html
│ │ │ ├── ui-scene-label.html
│ │ │ ├── ui-settings-content.html
│ │ │ ├── ui-settings.html
│ │ │ └── ui-target.html
│ │ └── maps/
│ │ ├── reldens-bots-forest-house-01-n0.json
│ │ ├── reldens-bots-forest.json
│ │ ├── reldens-bots.json
│ │ ├── reldens-forest.json
│ │ ├── reldens-gravity.json
│ │ ├── reldens-house-1-2d-floor.json
│ │ ├── reldens-house-1.json
│ │ ├── reldens-house-2.json
│ │ └── reldens-town.json
│ ├── browserconfig.xml
│ ├── config.js
│ ├── css/
│ │ ├── ads.scss
│ │ ├── base.scss
│ │ ├── chat.scss
│ │ ├── firebase.scss
│ │ ├── game-over.scss
│ │ ├── instructions.scss
│ │ ├── items-system.scss
│ │ ├── joystick.scss
│ │ ├── minimap.scss
│ │ ├── player-selection.scss
│ │ ├── player-stats-bars.scss
│ │ ├── rewards-events.scss
│ │ ├── scores.scss
│ │ ├── settings.scss
│ │ ├── skills.scss
│ │ ├── styles.scss
│ │ ├── teams.scss
│ │ ├── terms-and-conditions.scss
│ │ ├── variables.scss
│ │ └── wooden-ui.scss
│ ├── es-index.html
│ ├── index.html
│ ├── index.js
│ └── site.webmanifest
├── index.js.dist
└── plugins/
├── bot.js
├── client-plugin.js
├── objects/
│ ├── client/
│ │ └── npc1.js
│ └── server/
│ ├── healer.js
│ ├── quest-npc.js
│ └── weapons-master.js
└── server-plugin.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .claude/client-camera-follow-system.md
================================================
# Client Camera Follow System Architecture
This document describes how the camera follow system works in the Reldens client-side code.
## Overview
The camera follow system manages how the Phaser camera tracks the player character during gameplay. It involves multiple components across the client architecture: PlayerEngine, GameEngine, and scene management.
## Key Components
### 1. PlayerEngine (lib/users/client/player-engine.js)
**Purpose**: Manages the player character on the client-side, including camera initialization and configuration.
**Camera Configuration Properties** (lines 88-98):
```javascript
this.cameraRoundPixels = Boolean(
this.config.getWithoutLogs('client/general/engine/cameraRoundPixels', true)
);
this.cameraInterpolationX = Number(
this.config.getWithoutLogs('client/general/engine/cameraInterpolationX', 0.04)
);
this.cameraInterpolationY = Number(
this.config.getWithoutLogs('client/general/engine/cameraInterpolationY', 0.04)
);
```
**Configuration Source**: These values come from the database `config` table with scope `client` and are loaded during game initialization.
### 2. Camera Initialization Flow (PlayerEngine.create())
**Execution Order** (lines 115-139):
1. **Player Sprite Creation** (line 126):
- `this.addPlayer(this.playerId, addPlayerData)` creates the player sprite in the physics world
2. **Initial Camera Follow** (line 127):
- `this.scene.cameras.main.startFollow(this.players[this.playerId])`
- Camera begins tracking the player sprite
3. **Scene Visibility** (line 128):
- `this.scene.scene.setVisible(true, this.roomName)` makes the scene visible
4. **Camera Fade-In Effect** (line 129):
- `this.scene.cameras.main.fadeFrom(this.fadeDuration)`
- Starts fade-in animation (default 1000ms duration)
5. **Physics World Configuration** (lines 130-132):
- `fixedStep = false` enables variable physics timestep
- Sets physics and camera bounds to match map dimensions
6. **Camera Fade Complete Handler** (lines 134-138):
- Event listener triggered when fade animation completes
- Re-initializes camera follow with interpolation settings
- Sets lerp and roundPixels values
### 3. Phaser Camera Follow API
**startFollow() Method Signature**:
```javascript
camera.startFollow(target, roundPixels, lerpX, lerpY, offsetX, offsetY)
```
**Parameters**:
- `target`: The game object (player sprite) to follow
- `roundPixels` (optional): Boolean - force pixel-perfect rendering
- `lerpX` (optional): Number - horizontal interpolation (0-1, default 1)
- `lerpY` (optional): Number - vertical interpolation (0-1, default 1)
- `offsetX` (optional): Number - horizontal offset from target center
- `offsetY` (optional): Number - vertical offset from target center
**Lerp Behavior**:
- Value of `1`: Camera instantly snaps to target position (no interpolation)
- Value < `1`: Camera smoothly interpolates to target position
- Lower values (e.g., 0.04) = slower, smoother camera movement
- Higher values (e.g., 0.8) = faster, more responsive camera movement
### 4. GameEngine.updateGameSize() Integration
**Purpose** (lib/game/client/game-engine.js:79-106): Handles responsive behavior when window resizes or fullscreen toggles.
**Camera Lerp Adjustment** (lines 84-86, 101-104):
```javascript
if(player){
activeScene.cameras.main.setLerp(player.cameraInterpolationX, player.cameraInterpolationY);
}
```
**Execution Flow**:
1. **Before resize operations** (line 85): Sets lerp values
2. **Timeout delay** (line 87): Waits for configured duration (default 500ms)
3. **After resize operations** (line 103): Restores lerp values
**Why Twice?**:
- First call: Prepares camera for UI element repositioning
- Second call: Ensures camera tracking restored after all resize operations complete
### 5. Event-Driven Architecture
**Scene Creation Event** (game-manager.js:248):
```javascript
this.events.on('reldens.afterSceneDynamicCreate', async () => {
this.gameEngine.updateGameSize(this);
});
```
**Timing Sequence**:
1. Scene created
2. PlayerEngine.create() called - camera follow initialized
3. Camera fade starts (1000ms)
4. `reldens.afterSceneDynamicCreate` event fires
5. `updateGameSize()` called - adjusts camera lerp
6. Camera fade completes - lerp values set in event handler
### 6. Configuration Values
**Database Config Paths**:
- `client/general/engine/cameraRoundPixels`: Boolean (default: true)
- `client/general/engine/cameraInterpolationX`: Float (default: 0.04)
- `client/general/engine/cameraInterpolationY`: Float (default: 0.04)
- `client/players/animations/fadeDuration`: Integer milliseconds (default: 1000)
- `client/general/gameEngine/updateGameSizeTimeOut`: Integer milliseconds (default: 500)
**Config Loading**: Values are loaded from database during server initialization and sent to client in the `START_GAME` message as part of `gameConfig`.
### 7. Physics World Integration
**Fixed Step Setting** (player-engine.js:130):
```javascript
this.scene.physics.world.fixedStep = false;
```
**Impact**:
- `false`: Variable timestep - physics updates based on actual frame time
- `true`: Fixed timestep - physics updates at consistent intervals regardless of frame rate
**Camera Bounds** (lines 131-132):
```javascript
this.scene.physics.world.setBounds(0, 0, this.scene.map.widthInPixels, this.scene.map.heightInPixels);
this.scene.cameras.main.setBounds(0, 0, this.scene.map.widthInPixels, this.scene.map.heightInPixels);
```
Both physics world and camera are constrained to the map dimensions to prevent the camera from showing areas outside the game world.
### 8. Responsive Behavior
**Window Resize Listener** (game-manager.js:253-255):
```javascript
this.gameDom.getWindow().addEventListener('resize', () => {
this.gameEngine.updateGameSize(this);
});
```
**Fullscreen Handlers** (handlers/full-screen-handler.js:57, 65):
- Entering fullscreen: `updateGameSize()` called
- Exiting fullscreen: `updateGameSize()` called
**Purpose**: Ensures camera interpolation remains consistent across different viewport sizes and display modes.
## Data Flow Summary
1. Database Config
2. Server loads config
3. Client receives config in START_GAME message
4. PlayerEngine constructor reads config values
5. PlayerEngine.create() initializes camera
6. startFollow() begins tracking player
7. Fade animation starts
8. Camera fade completes - lerp values applied
9. Window resize events - updateGameSize() maintains lerp
## Key Technical Points
1. **Camera initialization happens in two phases**: Initial `startFollow()` and post-fade configuration
2. **Lerp values must be passed to `startFollow()` or set via `setLerp()`** for interpolation to work
3. **Round pixels and lerp work together**: Round pixels prevents sub-pixel jitter, lerp provides smooth motion
4. **Physics timestep affects camera smoothness**: Variable timestep can cause frame-to-frame variations
5. **Responsive system maintains camera settings**: `updateGameSize()` ensures lerp persists through viewport changes
## File Locations
- **PlayerEngine**: `lib/users/client/player-engine.js`
- **GameEngine**: `lib/game/client/game-engine.js`
- **GameManager**: `lib/game/client/game-manager.js`
- **FullScreenHandler**: `lib/game/client/handlers/full-screen-handler.js`
- **Config Database**: `config` table with `scope='client'`
================================================
FILE: .claude/commands-reference.md
================================================
# Commands Reference
Complete reference for all Reldens CLI commands.
## CLI Binaries
The project provides three main CLI entry points:
- `reldens` - Main command router (bin/reldens-commands.js)
- `reldens-generate` - Data generation tool (bin/generate.js)
- `reldens-import` - Data import tool (bin/import.js)
## Development & Building
```bash
# Run tests
npm test
# Or with filters
node tests/manager.js --filter="test-name" --break-on-error
# Build commands (via reldens CLI)
reldens buildCss [theme-name] # Build theme styles
reldens buildClient [theme-name] # Build client HTML
reldens buildSkeleton # Build both styles and client
reldens fullRebuild # Complete rebuild from scratch
# Theme & asset management
reldens installDefaultTheme # Install default theme
reldens copyAssetsToDist # Copy assets to dist folder
reldens copyDefaultAssets # Copy default assets to dist/assets
reldens copyDefaultTheme # Copy default theme to project
reldens copyPackage # Copy reldens module packages to project
reldens resetDist # Delete and recreate dist folder
reldens removeDist # Delete dist folder only
# Database & entities
reldens generateEntities [--override] # Generate entities from database schema
# This reads .env credentials and uses @reldens/storage to generate entities
# Generated entities are placed in the generated-entities/ directory
# Direct entity generation with connection arguments (bypasses .env):
npx reldens-storage generateEntities --user=reldens --pass=reldens --database=reldens_clean --driver=objection-js
```
## Prisma-Specific Commands
**IMPORTANT:** Prisma requires a separate client generation step before entities can be generated.
```bash
# Step 1: Generate Prisma schema and client from existing database
# This introspects the database and creates prisma/schema.prisma + prisma/client/
npx reldens-storage-prisma --host=localhost --port=3306 --user=reldens --password=reldens --database=reldens_clean --clientOutputPath=./client
# Step 2: Generate Reldens entities using Prisma driver
npx reldens-storage generateEntities --user=reldens --pass=reldens --database=reldens_clean --driver=prisma
# Full parameter list for reldens-storage-prisma:
# --host Database host (default: localhost)
# --port Database port (default: 3306)
# --user Database username (required)
# --password Database password (required)
# --database Database name (required)
# --clientOutputPath Output path for Prisma client (default: ./client)
# --schemaPath Path for schema.prisma file (default: ./prisma)
```
**Prisma Workflow:**
1. Run `reldens-storage-prisma` to generate schema.prisma and Prisma client
2. The command introspects your MySQL database and creates the Prisma schema
3. Run `reldens-storage generateEntities` with `--driver=prisma` to generate Reldens entities
4. Set `RELDENS_STORAGE_DRIVER=prisma` in your `.env` file to use Prisma at runtime
**Environment Variables for Prisma:**
```
RELDENS_STORAGE_DRIVER=prisma
RELDENS_DB_URL=mysql://user:password@host:port/database
```
## Installation & Setup
```bash
reldens createApp # Create base project skeleton
reldens installSkeleton # Install skeleton
reldens copyEnvFile # Copy .env.dist template
reldens copyKnexFile # Copy knexfile.js template
reldens copyIndex # Copy index.js template
reldens copyServerFiles # Reset dist and run fullRebuild
reldens copyNew # Copy all default files for fullRebuild
reldens help # Show all available commands
reldens test # Test file system access
```
## Data Generation Tools
```bash
# Generate game data (via reldens-generate)
reldens-generate players-experience # Generate player XP per level
reldens-generate monsters-experience # Generate monster XP per level
reldens-generate attributes # Generate attributes per level
reldens-generate maps # Generate maps with various loaders
# Data import (via reldens-import)
reldens-import [data-type] # Import game data
```
## User Management Commands
```bash
# Create admin user
reldens createAdmin --user=username --pass=password --email=email@example.com
# Creates an admin user with role_id from config (default: 1)
# Validates email format and username/email uniqueness
# Password is automatically encrypted using PBKDF2 SHA-512
# Reset user password
reldens resetPassword --user=username --pass=newpassword
# Resets password for existing user
# Password is automatically encrypted
# Works for any user (admin or regular)
# Examples:
reldens createAdmin --user=admin --pass=SecurePass123 --email=admin@yourgame.com
reldens resetPassword --user=someuser --pass=NewSecurePass456
```
**Implementation Details:**
- Service classes: `CreateAdmin` and `ResetPassword` in `lib/users/server/`
- Both receive `serverManager` in constructor (following importer pattern)
- Services return boolean result with `error` property for failure details
- `createAdmin` uses existing `usersRepository.create()` with `role_id` in userData
- `resetPassword` uses `usersRepository.loadOneBy()` and `updateById()`
- Admin role ID from config: `server/admin/roleId` (default: 1)
- Email validation via `sc.validateInput(email, 'email')` from `@reldens/utils`
- Commands initialize ServerManager automatically from `.env` (pattern from `bin/import.js`)
- Password encryption uses `Encryptor` from `@reldens/server-utils` (100k iterations, SHA-512)
================================================
FILE: .claude/entities-reference.md
================================================
# Entities Reference
Complete list of all 60+ entity types in the Reldens platform.
Entities are located in `generated-entities/entities/` and are auto-generated from the database schema.
## Ads System
- ads
- ads-banner
- ads-event-video
- ads-played
- ads-providers
- ads-types
## Audio System
- audio
- audio-categories
- audio-markers
- audio-player-config
## Chat System
- chat
- chat-message-types
## Clans/Teams System
- clan
- clan-levels
- clan-levels-modifiers
- clan-members
## Configuration
- config
- config-types
## Drops/Rewards
- drops-animations
## Features
- features
## Items System
- items-group
- items-inventory
- items-item
- items-item-modifiers
- items-types
## Localization
- locale
- users-locale
## Objects System
- objects
- objects-animations
- objects-assets
- objects-items-inventory
- objects-items-requirements
- objects-items-rewards
- objects-skills
- objects-stats
- objects-types
## Operations
- operation-types
## Players
- players
- players-state
- players-stats
## Respawn System
- respawn
## Rewards System
- rewards
- rewards-events
- rewards-events-state
- rewards-modifiers
## Rooms/Maps
- rooms
- rooms-change-points
- rooms-return-points
## Scores/Leaderboards
- scores
- scores-detail
## Skills System
- skills-class-level-up-animations
- skills-class-path
- skills-class-path-level-labels
- skills-class-path-level-skills
- skills-groups
- skills-levels
- skills-levels-modifiers
- skills-levels-modifiers-conditions
- skills-levels-set
- skills-owners-class-path
- skills-skill
- skills-skill-animations
- skills-skill-attack
- skills-skill-group-relation
- skills-skill-owner-conditions
- skills-skill-owner-effects
- skills-skill-owner-effects-conditions
- skills-skill-physical-data
- skills-skill-target-effects
- skills-skill-target-effects-conditions
- skills-skill-type
## Snippets
- snippets
## Stats/Modifiers
- stats
- target-options
## Users/Authentication
- users
- users-login
## Entity Relations
Entity relations keys are defined in `generated-entities/entities-config.js`.
Custom entity overrides are located in `lib/[plugin-folder]/server/entities` or `lib/[plugin-folder]/server/models`.
================================================
FILE: .claude/environment-variables.md
================================================
# Environment Variables Reference
Complete reference for all RELDENS_* environment variables.
See `lib/game/server/install-templates/.env.dist` for the template file.
## Application Server
- `NODE_ENV` - Environment mode (production/development)
- `RELDENS_DEFAULT_ENCODING` - Default encoding (default: utf8)
- `RELDENS_APP_HOST` - Application host
- `RELDENS_APP_PORT` - Application port
- `RELDENS_PUBLIC_URL` - Public URL for the application
## HTTPS Configuration
- `RELDENS_EXPRESS_USE_HTTPS` - Enable HTTPS
- `RELDENS_EXPRESS_HTTPS_PRIVATE_KEY` - Private key path
- `RELDENS_EXPRESS_HTTPS_CERT` - Certificate path
- `RELDENS_EXPRESS_HTTPS_CHAIN` - Certificate chain path
- `RELDENS_EXPRESS_HTTPS_PASSPHRASE` - HTTPS passphrase
## Express Server
- `RELDENS_USE_EXPRESS_JSON` - Enable JSON parsing
- `RELDENS_EXPRESS_JSON_LIMIT` - JSON payload limit
- `RELDENS_EXPRESS_URLENCODED_LIMIT` - URL encoded limit
- `RELDENS_GLOBAL_RATE_LIMIT` - Global rate limiting
- `RELDENS_TOO_MANY_REQUESTS_MESSAGE` - Rate limit message
- `RELDENS_USE_URLENCODED` - Enable URL encoding
- `RELDENS_USE_HELMET` - Enable Helmet security
- `RELDENS_USE_XSS_PROTECTION` - Enable XSS protection
- `RELDENS_USE_CORS` - Enable CORS
- `RELDENS_CORS_ORIGIN` - CORS origin
- `RELDENS_CORS_METHODS` - CORS methods
- `RELDENS_CORS_HEADERS` - CORS headers
- `RELDENS_EXPRESS_SERVE_HOME` - Serve dynamic home page
- `RELDENS_EXPRESS_TRUSTED_PROXY` - Trusted proxy
- `RELDENS_EXPRESS_RATE_LIMIT_MS` - Rate limit window (default: 60000)
- `RELDENS_EXPRESS_RATE_LIMIT_MAX_REQUESTS` - Max requests per window (default: 30)
- `RELDENS_EXPRESS_RATE_LIMIT_APPLY_KEY_GENERATOR` - Apply key generator
- `RELDENS_EXPRESS_SERVE_STATICS` - Serve static files
## Admin Panel
- `RELDENS_ADMIN_ROUTE_PATH` - Admin panel route path
- `RELDENS_ADMIN_SECRET` - Admin authentication secret
- `RELDENS_HOT_PLUG` - Enable hot-plug configuration updates (0/1)
## Colyseus Monitor
- `RELDENS_MONITOR` - Enable Colyseus monitor
- `RELDENS_MONITOR_AUTH` - Enable monitor authentication
- `RELDENS_MONITOR_USER` - Monitor username
- `RELDENS_MONITOR_PASS` - Monitor password
## Storage & Database
- `RELDENS_STORAGE_DRIVER` - Storage driver (objection-js, mikro-orm, prisma)
- `RELDENS_DB_CLIENT` - Database client (mysql, mysql2, mongodb)
- `RELDENS_DB_HOST` - Database host
- `RELDENS_DB_PORT` - Database port
- `RELDENS_DB_NAME` - Database name
- `RELDENS_DB_USER` - Database username
- `RELDENS_DB_PASSWORD` - Database password
- `RELDENS_DB_POOL_MIN` - Connection pool minimum (default: 2)
- `RELDENS_DB_POOL_MAX` - Connection pool maximum (default: 10)
- `RELDENS_DB_LIMIT` - Query limit (default: 0)
- `RELDENS_DB_URL` - Full database URL (auto-generated if not specified)
- `RELDENS_DB_URL_OPTIONS` - Additional URL options
## Logging
- `RELDENS_LOG_LEVEL` - Log level (0-7, default: 7)
- `RELDENS_ENABLE_TRACE_FOR` - Enable trace for specific levels (emergency,alert,critical)
## Mailer
- `RELDENS_MAILER_ENABLE` - Enable email functionality
- `RELDENS_MAILER_SERVICE` - Mail service provider
- `RELDENS_MAILER_HOST` - SMTP host
- `RELDENS_MAILER_PORT` - SMTP port
- `RELDENS_MAILER_USER` - SMTP username
- `RELDENS_MAILER_PASS` - SMTP password
- `RELDENS_MAILER_FROM` - From email address
- `RELDENS_MAILER_FORGOT_PASSWORD_LIMIT` - Forgot password attempts limit (default: 4)
## Bundler
- `RELDENS_ALLOW_RUN_BUNDLER` - Allow automatic bundler execution via createClientBundle() (default: 0)
- `RELDENS_ALLOW_BUILD_CLIENT` - Allow client build execution via buildClient() (default: 1)
- `RELDENS_ALLOW_BUILD_CSS` - Allow CSS build execution via buildCss() (default: 1)
- `RELDENS_FORCE_RESET_DIST_ON_BUNDLE` - Force reset dist on bundle
- `RELDENS_FORCE_COPY_ASSETS_ON_BUNDLE` - Force copy assets on bundle
- `RELDENS_JS_SOURCEMAPS` - Enable JavaScript source maps
- `RELDENS_CSS_SOURCEMAPS` - Enable CSS source maps
**Important**: Always use `createClientBundle()` instead of calling `buildClient()` directly when building during server startup. The `createClientBundle()` method respects `RELDENS_ALLOW_RUN_BUNDLER` and provides additional configuration options.
## Game Server
- `RELDENS_PING_INTERVAL` - Ping interval in ms (default: 5000)
- `RELDENS_PING_MAX_RETRIES` - Max ping retries (default: 3)
## Firebase
- `RELDENS_FIREBASE_ENABLE` - Enable Firebase authentication
- `RELDENS_FIREBASE_API_KEY` - Firebase API key
- `RELDENS_FIREBASE_APP_ID` - Firebase app ID
- `RELDENS_FIREBASE_AUTH_DOMAIN` - Firebase auth domain
- `RELDENS_FIREBASE_DATABASE_URL` - Firebase database URL
- `RELDENS_FIREBASE_PROJECT_ID` - Firebase project ID
- `RELDENS_FIREBASE_STORAGE_BUCKET` - Firebase storage bucket
- `RELDENS_FIREBASE_MESSAGING_SENDER_ID` - Firebase sender ID
- `RELDENS_FIREBASE_MEASUREMENTID` - Firebase measurement ID
================================================
FILE: .claude/feature-modules.md
================================================
# Feature Modules Reference
Complete reference for all 23 feature modules under `lib/`.
## Core/Game Management
### Game (`lib/game/`)
Core game engine
- ServerManager - Main server orchestrator (lib/game/server/manager.js)
- GameManager - Main client orchestrator (lib/game/client/game-manager.js)
- Data server configuration
- Entities loader
- Maps loader
- Login manager
- Installation scripts
- Theme manager
### Rooms (`lib/rooms/`)
Core multiplayer room system
- `server/scene.js` (RoomScene): Main game room with physics, collisions, objects
- `server/login.js` (RoomLogin): Authentication and player initialization
- Client connects via `room-events.js` to handle server state synchronization
### World (`lib/world/`)
Physics engine integration (P2.js), pathfinding, collisions
- Authoritative physics calculations
- Collision detection and handling
- Pathfinding algorithms
### Config (`lib/config/`)
Configuration management
- Database-driven configuration
- Environment variable handling
- Runtime configuration overrides
### Features (`lib/features/`)
Plugin-like modular system
- Features are loaded from database (`features` table with `is_enabled` flag)
- `server/manager.js` (FeaturesManager) dynamically loads enabled features
- Each feature can hook into events via `setup()` method
## Gameplay Systems
### Actions (`lib/actions/`)
Combat system (PvP/PvE), skills, battle mechanics
- Server handles authoritative battle calculations
- Client receives battle states and renders animations
- `server/battle.js` - Main battle system
- `server/pve.js` - PvE combat logic
- `server/pvp.js` - PvP combat logic
### Inventory (`lib/inventory/`)
Items system with equipment and usable items
- Integrates with @reldens/items-system
- Item management, equipment slots, consumables
### Respawn (`lib/respawn/`)
Player and NPC respawn system
- Death handling
- Respawn points configuration
### Rewards (`lib/rewards/`)
Loot and rewards system
- Drop tables
- Reward distribution
### Scores (`lib/scores/`)
Leaderboards and ranking system
- Player scores tracking
- Global leaderboards
### Teams (`lib/teams/`)
Party/guild system
- Team formation
- Shared objectives
- Clan levels and bonuses
## Player Systems
### Users (`lib/users/`)
Authentication, registration, player management
- Supports guest users, Firebase authentication
- `server/login-manager.js` handles all auth flows
- Player creation and management
### Chat (`lib/chat/`)
Multi-channel chat (global, room, private messages)
- Message types and tabs
- Real-time messaging
### Audio (`lib/audio/`)
Sound and music system
- Background music management
- Sound effects for actions and events
- Audio configuration per scene/room
### Prediction (`lib/prediction/`)
Client-side prediction system
- Reduces perceived latency
- Smooths player movement
## Integration/Support
### Admin (`lib/admin/`)
Admin panel integration with @reldens/cms
- Manages game configuration through web interface
- Handles entity CRUD operations
- Supports hot-plug configuration updates
### Firebase (`lib/firebase/`)
Firebase integration
- Firebase authentication
- Client-side Firebase SDK integration
### Ads (`lib/ads/`)
Advertisement integration system
- Third-party ad network support (CrazyGames, GameMonetize)
- Ad placement configuration
### Import (`lib/import/`)
Data import utilities
- File handlers
- MIME type detection
- Bulk data import tools
### Objects (`lib/objects/`)
Game objects (NPCs, interactables, respawn areas)
- `server/manager.js` loads and manages room objects
- Objects can listen to messages via `listenMessages` interface
### Snippets (`lib/snippets/`)
Reusable code snippets and utilities
- Common helper functions
- Shared utilities across modules
### Bundlers (`lib/bundlers/`)
Asset bundling drivers
- Parcel integration
- CSS and JavaScript bundling
- Theme asset compilation
================================================
FILE: .claude/guest-system-technical-guide.md
================================================
# Guest System Technical Guide
## Overview
The guest system allows anonymous players to join the game without registration. This document explains the complete technical flow from database configuration to client-side form activation.
---
## 1. Database Configuration
### Rooms Table - `customData` Field
Each room can be marked as guest-accessible via the `customData` JSON field:
```json
{
"allowGuest": true
}
```
**Location:** `rooms` table in `customData` column
**Example SQL:**
```sql
UPDATE rooms SET customData = '{"allowGuest": true}' WHERE name = 'town';
```
---
## 2. Server-Side Flow
### 2.1 Rooms Loading (`lib/rooms/server/manager.js`)
**Method:** `loadRooms()` (lines 204-241)
```javascript
async loadRooms(){
let roomsModels = await this.dataServer.getEntity('rooms').loadAllWithRelations([...]);
// Process each room
for(let room of roomsModels){
let roomModel = this.generateRoomModel(room);
rooms.push(roomModel);
roomsById[room.id] = roomModel;
roomsByName[room.name] = roomModel;
}
// Filter guest rooms
this.availableRoomsGuest = this.filterGuestRooms(roomsByName);
// Create room lists for registration and login
let registrationRooms = this.filterRooms(true);
this.registrationAvailableRooms = this.extractRoomDataForSelector(registrationRooms);
this.registrationAvailableRoomsGuest = this.extractRoomDataForSelector(
this.fetchGuestRooms(registrationRooms)
);
let loginRooms = this.filterRooms(false);
this.loginAvailableRooms = this.extractRoomDataForSelector(loginRooms);
this.loginAvailableRoomsGuest = this.extractRoomDataForSelector(
this.fetchGuestRooms(loginRooms)
);
return this.loadedRooms;
}
```
### 2.2 Guest Room Filtering (`lib/rooms/server/manager.js`)
**Method:** `filterGuestRooms()` (line 415+)
```javascript
filterGuestRooms(availableRooms){
let guestRooms = {};
for(let roomName of Object.keys(availableRooms)){
let room = availableRooms[roomName];
let customData = sc.get(room, 'customData', {});
if(sc.isString(customData)){
customData = JSON.parse(customData);
}
// Check if allowGuest is true
if(sc.get(customData, 'allowGuest')){
guestRooms[roomName] = room;
}
}
return guestRooms;
}
```
**Method:** `fetchGuestRooms()` (line 403+)
```javascript
fetchGuestRooms(availableRooms){
// Check global setting
if(this.allowGuestOnRooms){
return availableRooms; // All rooms allow guests
}
// Filter by room-specific allowGuest
return this.filterGuestRooms(availableRooms);
}
```
**Global Setting:**
- Config path: `server/players/guestUser/allowOnRooms`
- Default: `true`
- If `true`, all rooms allow guests
- If `false`, only rooms with `customData.allowGuest = true` allow guests
### 2.3 Config Assignment (`lib/rooms/server/manager.js`)
**Method:** `defineRoomsInGameServer()` (lines 109-116)
```javascript
// After all rooms are loaded and defined
if(this.config.client?.rooms?.selection){
this.config.client.rooms.selection.availableRooms = {
registration: this.registrationAvailableRooms,
registrationGuest: this.registrationAvailableRoomsGuest, // ← Guest rooms here
login: this.loginAvailableRooms,
loginGuest: this.loginAvailableRoomsGuest // ← Guest rooms here
};
}
```
**Called by:** `ServerManager.defineServerRooms()` calls `RoomsManager.defineRoomsInGameServer()`
---
## 3. Config File Generation
### 3.1 Timing (CRITICAL)
**File:** `lib/game/server/manager.js`
**Execution order:**
1. `initializeManagers()` (line 261-263)
- Calls `defineServerRooms()`
- Guest rooms configured in `this.configManager.client.rooms.selection.availableRooms`
2. **Config file created** (line 264-272)
- `HomepageLoader.createConfigFile()` with guest rooms data
3. **Client built** (line 272)
- Bundles config.js into dist folder
### 3.2 Config File Creation (`lib/game/server/homepage-loader.js`)
**Method:** `createConfigFile()` (lines 51-62)
```javascript
static createConfigFile(projectThemePath, initialConfiguration){
let configFilePath = FileHandler.joinPaths(projectThemePath, 'config.js');
let configFileContents = 'window.reldensInitialConfig = '+JSON.stringify(initialConfiguration)+';';
let writeResult = FileHandler.writeFile(configFilePath, configFileContents);
if(!writeResult){
Logger.error('Failed to write config file: '+configFilePath);
return false;
}
Logger.info('Config file created: '+configFilePath);
return true;
}
```
**Output file:** `theme/config.js`
**Content structure:**
```javascript
window.reldensInitialConfig = {
gameEngine: { /* ... */ },
client: {
rooms: {
selection: {
availableRooms: {
registration: { /* normal rooms */ },
registrationGuest: { /* guest-allowed rooms */ }, // ← KEY DATA
login: { /* normal rooms */ },
loginGuest: { /* guest-allowed rooms */ } // ← KEY DATA
}
}
}
}
};
```
---
## 4. Client-Side Flow
### 4.1 Config Loading (`lib/game/client/game-manager.js`)
**Constructor** (line 48-94)
```javascript
constructor(){
this.config = new ConfigManager();
let initialConfig = this.gameDom.getWindow()?.reldensInitialConfig || {};
sc.deepMergeProperties(this.config, initialConfig); // ← Loads from window.reldensInitialConfig
// ...
}
```
**Data source:** `window.reldensInitialConfig` from `theme/config.js`
### 4.2 Client Start (`lib/game/client/handlers/client-start-handler.js`)
**Method:** `clientStart()` (line 30-53)
```javascript
clientStart(){
let registrationForm = new RegistrationFormHandler(this.gameManager);
registrationForm.activateRegistration();
let guestForm = new GuestFormHandler(this.gameManager); // ← Guest handler
guestForm.activateGuest(); // ← Activates guest form
// ... other handlers
}
```
**Called by:** `GameManager.clientStart()` on `DOMContentLoaded`
### 4.3 Guest Form Activation (`lib/game/client/handlers/guest-form-handler.js`)
**Method:** `activateGuest()` (lines 34-72)
```javascript
activateGuest(){
if(!this.form){
return false;
}
// Get guest rooms from config
let availableGuestRooms = this.gameManager.config.getWithoutLogs(
'client/rooms/selection/availableRooms/registrationGuest', // ← Config path
{}
);
// Check if guest login is allowed AND guest rooms exist
if(
!this.gameManager.config.get('client/general/users/allowGuest')
|| 0 === Object.keys(availableGuestRooms).length // ← CRITICAL CHECK
){
this.form.classList.add('hidden'); // ← HIDE FORM
return true;
}
// Form is visible, activate submit handler
this.form.addEventListener('submit', (e) => {
e.preventDefault();
if(!this.form.checkValidity()){
return false;
}
this.form.querySelector(selectors.LOADING_CONTAINER).classList.remove(GameConst.CLASSES.HIDDEN);
let randomGuestName = 'guest-'+sc.randomChars(12);
let userName = this.gameManager.config.getWithoutLogs('client/general/users/allowGuestUserName', false)
? this.gameDom.getElement(selectors.GUEST.USERNAME).value
: randomGuestName;
let formData = {
formId: this.form.id,
username: userName,
password: userName,
rePassword: userName,
isGuest: true
};
this.gameManager.startGame(formData, true);
});
return true;
}
```
**Form element:** `#guest-form` in `theme/default/index.html`
**Key logic:**
- If `availableGuestRooms` is empty: form hidden
- If `client/general/users/allowGuest` is false: form hidden
- Otherwise: form visible and functional
---
## 5. Complete Flow Diagram
**Step 1: DATABASE (rooms table)**
- customData: {"allowGuest": true}
**Step 2: SERVER - RoomsManager.loadRooms()**
- Loads all rooms from database
- Calls filterGuestRooms() to identify guest-allowed rooms
- Creates registrationAvailableRoomsGuest list
**Step 3: SERVER - RoomsManager.defineRoomsInGameServer()**
- Assigns guest rooms to config:
- config.client.rooms.selection.availableRooms = {
- registrationGuest: [...],
- loginGuest: [...]
- }
**Step 4: SERVER - ServerManager.startGameServerInstance()**
- After initializeManagers() completes
- Calls HomepageLoader.createConfigFile()
- Writes theme/config.js with guest rooms data
- Calls themeManager.buildClient()
- Bundles config.js into dist/
**Step 5: CLIENT - Browser loads theme/default/index.html**
- Includes script src="config.js"
- Sets window.reldensInitialConfig
**Step 6: CLIENT - GameManager constructor**
- Reads window.reldensInitialConfig
- Merges into this.config
**Step 7: CLIENT - ClientStartHandler.clientStart()**
- Creates GuestFormHandler
- Calls activateGuest()
**Step 8: CLIENT - GuestFormHandler.activateGuest()**
- Reads config.get('client/rooms/selection/availableRooms/registrationGuest')
- If empty: HIDE form
- If not empty: SHOW form and attach submit handler
---
## 6. Configuration Options
### Server-Side Configs
**Path:** `server/players/guestUser/allowOnRooms`
- **Type:** Boolean
- **Default:** `true`
- **Effect:** If `true`, all rooms allow guests (ignores `customData.allowGuest`)
**Path:** `server/players/guestsUser/emailDomain`
- **Type:** String
- **Default:** `@guest-reldens.com`
- **Effect:** Email domain for guest accounts
### Client-Side Configs
**Path:** `client/general/users/allowGuest`
- **Type:** Boolean
- **Default:** Set from server config
- **Effect:** Master switch for guest login feature
**Path:** `client/general/users/allowGuestUserName`
- **Type:** Boolean
- **Default:** `false`
- **Effect:** If `true`, allows guests to choose username; if `false`, generates random username
### Environment Variables
**Variable:** `RELDENS_CREATE_CONFIG_FILE`
- **Type:** Number (0 or 1)
- **Default:** `1`
- **Effect:** Controls whether config.js file is created after rooms are configured
**Variable:** `RELDENS_GUESTS_EMAIL_DOMAIN`
- **Type:** String
- **Default:** `@guest-reldens.com`
- **Effect:** Email domain for guest user accounts
---
## 7. Testing Guest System
### Database Setup
```sql
-- Enable guest on specific room
UPDATE rooms
SET customData = '{"allowGuest": true}'
WHERE name = 'town';
-- Disable guest on specific room
UPDATE rooms
SET customData = '{"allowGuest": false}'
WHERE name = 'forest';
```
---
## 8. Code References
**Key Files:**
- `lib/rooms/server/manager.js` - Room loading and guest filtering
- `lib/game/server/manager.js` - Config file creation timing
- `lib/game/server/homepage-loader.js` - Config file generation
- `lib/game/client/game-manager.js` - Config loading
- `lib/game/client/handlers/client-start-handler.js` - Form initialization
- `lib/game/client/handlers/guest-form-handler.js` - Guest form logic
**Database:**
- Table: `rooms`
- Column: `customData` (JSON)
- Field: `allowGuest` (boolean)
**Config Paths:**
- Server: `server/players/guestUser/allowOnRooms`
- Server: `server/players/guestsUser/emailDomain`
- Client: `client/general/users/allowGuest`
- Client: `client/general/users/allowGuestUserName`
- Client: `client/rooms/selection/availableRooms/registrationGuest`
- Client: `client/rooms/selection/availableRooms/loginGuest`
================================================
FILE: .claude/installer-guide.md
================================================
# Installer Guide
Complete guide for the Reldens web-based installation wizard.
## Overview
The Reldens installer (`lib/game/server/installer.js`) provides a web-based GUI for setting up new Reldens installations. It handles database setup, entity generation, storage driver configuration, and project file creation.
## Accessing the Installer
The installer runs automatically on the first launch when no installation lock file exists:
```bash
npm start
# Navigate to http://localhost:8080 (or configured host/port)
```
The installer will automatically redirect to the installation wizard if the project has not been installed yet.
## Storage Drivers & Database Clients
Reldens supports three storage drivers with multiple database clients:
### Prisma Driver
- **mysql** - MySQL database (automated installation)
- **postgresql (manual)** - PostgreSQL database
- **sqlite (manual)** - SQLite database
- **sqlserver (manual)** - SQL Server database
- **mongodb (manual)** - MongoDB database
- **cockroachdb (manual)** - CockroachDB database
### Objection-js Driver (Knex.js)
- **mysql (native)** - MySQL with native driver (automated installation)
- **mysql2 (recommended)** - MySQL with mysql2 driver (automated installation)
- **pg (manual)** - PostgreSQL
- **sqlite3 (manual)** - SQLite3
- **better-sqlite3 (manual)** - Better-SQLite3
- **mssql (manual)** - SQL Server
- **oracledb (manual)** - Oracle DB
- **cockroachdb (manual)** - CockroachDB
### MikroORM Driver
- **mysql** - MySQL database (automated installation)
- **mariadb (manual)** - MariaDB database
- **postgresql (manual)** - PostgreSQL database
- **sqlite (manual)** - SQLite database
- **mongodb (manual)** - MongoDB database
- **mssql (manual)** - SQL Server
- **better-sqlite3 (manual)** - Better-SQLite3
## Automated vs Manual Installation
### Automated Installation (MySQL Only)
Only MySQL clients support automated installation scripts:
- `mysql` (all drivers)
- `mysql2` (objection-js only)
**Automated steps:**
1. Creates database tables via `reldens-install-v4.0.0.sql`
2. Installs basic configuration via `reldens-basic-config-v4.0.0.sql` (if checked)
3. Installs sample data via `reldens-sample-data-v4.0.0.sql` (if checked)
4. Generates entities from database schema
5. Creates project configuration files
### Manual Installation (All Other Clients)
Clients marked with **(manual)** require manual database setup:
- PostgreSQL, SQLite, MongoDB, SQL Server, Oracle, CockroachDB, MariaDB, Better-SQLite3
**Manual steps:**
1. Installer skips SQL script execution
2. User must manually create database tables and schema
3. Installer generates entities from existing database
4. Installer creates project configuration files
**Manual Setup Process:**
1. Select a manual client from the installer
2. Complete the installation wizard
3. Manually execute SQL scripts or create schema in your database:
- Copy SQL files from `migrations/production/` directory
- Adapt SQL syntax for your database (if needed)
- Execute scripts in order: install, basic-config, sample-data
4. Run entity generation: `reldens generateEntities --override`
5. Restart the application
## Installation Process Flow
### For MySQL Clients
1. **Package Installation** (if enabled)
- Status: "Checking and installing required packages..."
- Installs `@reldens/storage` and driver-specific packages
2. **Database Connection**
- Status: "Configuring database connection..."
- Tests connection with provided credentials
3. **Driver Installation**
- Status: "Installing database driver: {driver}..."
- Executes SQL migration scripts
- Creates tables, basic config, sample data
4. **Entity Generation**
- Status: "Generating entities from database schema..."
- Introspects database and generates entity classes
5. **Project Files**
- Status: "Creating project files..."
- Creates `.env`, `knexfile.js`, `index.js`, etc.
6. **Completion**
- Status: "Installation completed successfully!"
- Redirects to game
### For Manual Clients
1. **Package Installation** (if enabled)
2. **Database Connection**
3. **Driver Installation**
- Status: "Installing database driver: {driver}..."
- Logs: "Non-MySQL client detected ({client}), skipping automated SQL scripts."
- Skips all SQL migrations
4. **Entity Generation** (requires pre-existing database schema)
5. **Project Files**
6. **Completion**
## Status Tracking
The installer provides real-time status updates during installation:
- Status file: `dist/assets/install-status.json`
- Format: `{message: string, timestamp: number}`
- Frontend polls every 2 seconds
- Status messages appear beside/below loading image
**Status Messages:**
- "Starting installation process..."
- "Checking and installing required packages..."
- "Configuring database connection..."
- "Installing database driver: {driver}..."
- "Generating entities from database schema..."
- "Creating project files..."
- "Installation completed successfully!"
## Configuration Options
### App Settings
- **Host** - Server host URL (e.g., http://localhost)
- **Port** - Server port (default: 8080)
- **Public URL** - Public-facing URL (for reverse proxies)
- **Trusted Proxy** - Reverse proxy address
- **Admin Panel Path** - Admin interface route (default: /reldens-admin)
- **Admin Panel Secret Key** - Secret key for admin access
- **Hot-Plug** - Enable runtime configuration reload
### Storage Settings
- **Storage Driver** - Database ORM (prisma, objection-js, mikro-orm)
- **Client** - Database client library (see list above)
- **Host** - Database server host
- **Port** - Database server port
- **Database Name** - Database name
- **Username** - Database user
- **Password** - Database password
- **Install minimal configuration** - MySQL only
- **Install sample data** - MySQL only
### Optional Features
- **HTTPS** - SSL/TLS configuration
- **Monitor** - Colyseus monitoring tools
- **Mailer** - Email service integration (SendGrid, NodeMailer)
- **Firebase** - Firebase authentication integration
## Installer Architecture
### Core Classes
**Installer** (`lib/game/server/installer.js`)
- Main orchestration class
- Handles Express routes and form processing
- Coordinates sub-installers
- Manages status tracking
**GenericDriverInstallation** (`lib/game/server/installer/generic-driver-installation.js`)
- Handles ObjectionJS and MikroORM installations
- Executes SQL migrations via `rawQuery()`
- Checks client type and skips non-MySQL scripts
**PrismaInstallation** (`lib/game/server/installer/prisma-installation.js`)
- Handles Prisma-specific installation
- Runs installation in forked subprocess
- Generates Prisma schema and client
**PrismaSubprocessWorker** (`lib/game/server/installer/prisma-subprocess-worker.js`)
- Forked child process for Prisma installation
- Isolates Prisma client to avoid module caching
- Checks client type and skips non-MySQL scripts
**EntitiesInstallation** (`lib/game/server/installer/entities-installation.js`)
- Generates entity classes from database schema
- Supports all three storage drivers
**ProjectFilesCreation** (`lib/game/server/installer/project-files-creation.js`)
- Creates `.env` file with configuration
- Creates `knexfile.js` for ObjectionJS
- Creates `index.js` entry point
- Copies theme files and assets
**PackagesInstallation** (`lib/game/server/installer/packages-installation.js`)
- Manages npm package installation and linking based on `RELDENS_INSTALLATION_TYPE`
- Reads the lock file at construction time (while the main package link is still active)
- Runs installs before links so the main package link is always restored last
- Handles driver-specific dependencies (e.g. `@prisma/client` for Prisma)
**Installation Types** (set via `RELDENS_INSTALLATION_TYPE` environment variable):
- `normal` — installs `reldens` from npm registry; no linking
- `link` — npm links `reldens` and all `@reldens/*` packages; no npm installs
- `link-main` — npm installs all `@reldens/*` packages from registry (no version pinning), then npm links `reldens` last to restore the local source junction
**Package installation sequence** (`link-main`):
1. Lock file is read from `node_modules/reldens/package-lock.json` at construction (while link is active)
2. `unlinkAllPackages()` removes all existing links for `reldens` and all `@reldens/*` packages
3. `checkAndInstallPackages()` runs installs first, then the link:
- `npm install @reldens/cms`, `npm install @reldens/storage`, etc. (no version pinning)
- `npm link reldens` — restores the junction to the local source last
4. With the junction restored, `migrations/production/` resolves correctly through the link to the local source SQL files
### Frontend Files
**install/index.html**
- Installation form with all configuration fields
- Client dropdown populated by JavaScript
- Form validation and submission
**install/index.js**
- Database client mapping (`DB_CLIENTS_MAP`)
- Dynamic client dropdown updates
- Status polling functionality
- Form submission handling
**install/css/styles.scss**
- Installer styling
## MySQL-Only Scripts
The following SQL migration files only work with MySQL:
- `migrations/production/reldens-install-v4.0.0.sql`
- `migrations/production/reldens-basic-config-v4.0.0.sql`
- `migrations/production/reldens-sample-data-v4.0.0.sql`
For other databases, these scripts must be manually adapted to the target database syntax.
## Troubleshooting
### "Non-MySQL client detected, skipping automated SQL scripts"
**Cause:** Selected a manual database client (PostgreSQL, SQLite, MongoDB, etc.)
**Solution:**
1. Complete the installer wizard
2. Manually set up database schema
3. Run entity generation
4. Restart application
### "Connection failed, please check the storage configuration"
**Cause:** Invalid database credentials or unreachable database server
**Solution:**
1. Verify database server is running
2. Check host, port, username, password
3. Ensure database exists
4. Check firewall/network settings
### "Entities generation failed"
**Cause:** Database schema not found or invalid
**Solution:**
1. For MySQL: Ensure installation scripts ran successfully
2. For manual clients: Verify you created all required tables
3. Check database connection
4. Ensure user has schema read permissions
### "Required packages installation failed"
**Cause:** npm install failed or network issues
**Solution:**
1. Check internet connection
2. Manually run: `npm install @reldens/storage`
3. For Prisma: `npm install @prisma/client`
4. Check npm logs for errors
## Post-Installation
After successful installation:
1. Application redirects to game
2. Lock file created at configured path
3. Installer becomes inaccessible
4. Use admin panel for further configuration
5. Access admin at configured path (default: /reldens-admin)
6. Use configured admin secret key for first login
## Re-installation
To re-run the installer:
1. Stop the application
2. Delete the installation lock file (location configured in ThemeManager)
3. Optionally drop and recreate database
4. Start application and navigate to installation wizard
================================================
FILE: .claude/items-system-implementation.md
================================================
# Items System Implementation - Complete Documentation
## Overview
The Reldens Items System manages player inventory, equipment, and item modifiers. It uses the `@reldens/items-system` package for core functionality and integrates with the `@reldens/modifiers` package for stat modifications.
## Architecture
### Core Components
1. **ItemsServer** (`@reldens/items-system`) - Server-side inventory manager
2. **Inventory** (`@reldens/items-system`) - Base inventory container
3. **ItemBase** - Base class for all items
4. **Equipment** - Specialized item type for equippable items
5. **Modifier** (`@reldens/modifiers`) - Handles stat modifications
6. **StorageObserver** - Persists inventory changes to database
### Directory Structure
**lib/inventory/**
- **client/** - Client-side inventory UI and rendering
- **server/** - Server-side inventory logic
- items-factory.js - Creates item instances from database models
- message-actions.js - Handles equip/unequip/trade messages
- models-manager.js - Database operations
- storage-observer.js - Event listeners for persistence
- plugin.js - Inventory feature plugin
- **subscribers/** - Event subscribers
- player-subscriber.js - Creates player inventory on login
- player-death-subscriber.js
- constants.js
## Item Creation Flow
### When Player Logs In
**Entry Point**: `lib/inventory/server/plugin.js` line 50-51
```javascript
this.events.on('reldens.createPlayerStatsAfter', async (client, userModel, currentPlayer, room) => {
await PlayerSubscriber.createPlayerInventory(client, currentPlayer, room, this.events, this.modelsManager);
});
```
**Sequence**:
1. **Player Stats Loaded** (`lib/users/server/plugin.js` lines 289-309)
- Stats loaded from `players_stats` table
- Set on `currentPlayer.stats` and `currentPlayer.statsBase`
- Event `reldens.createPlayerStatsAfter` fires
2. **Inventory Creation** (`lib/inventory/server/subscribers/player-subscriber.js` lines 30-63)
```javascript
let serverProps = {
owner: currentPlayer, // The player schema instance
client: new ClientWrapper({client, room}),
persistence: true,
ownerIdProperty: 'player_id',
eventsManager: events,
modelsManager: modelsManager,
itemClasses: {...},
groupClasses: {...},
itemsModelData: room.config.inventory.items
};
let inventoryServer = new ItemsServer(serverProps);
inventoryServer.dataServer = new StorageObserver(inventoryServer.manager, modelsManager);
```
3. **Items Loading** (`lib/inventory/server/storage-observer.js` lines 169-182)
```javascript
async loadOwnerItems(){
let itemsModels = await this.modelsManager.loadOwnerItems(this.manager.getOwnerId());
let itemsInstances = await ItemsFactory.fromModelsList(itemsModels, this.manager);
await this.manager.fireEvent(ItemsEvents.LOADED_OWNER_ITEMS, this, itemsInstances, itemsModels);
await this.manager.setItems(itemsInstances);
}
```
4. **Item Instance Creation** (`lib/inventory/server/items-factory.js` lines 40-71)
```javascript
static async fromModel(itemInventoryModel, manager){
let itemClass = sc.get(
manager.itemClasses,
itemInventoryModel.related_items_item.key,
manager.types.classByTypeId(itemInventoryModel.related_items_item.type)
);
let itemObj = new itemClass(itemProps);
if (itemObj.isType(ItemsConst.TYPES.EQUIPMENT)) {
itemObj.equipped = (1 === itemInventoryModel.is_active); // Mark as equipped if active
}
await this.enrichWithModifiers(itemInventoryModel, itemObj, manager);
return itemObj;
}
```
5. **Modifier Creation** (`lib/inventory/server/items-factory.js` lines 79-93)
```javascript
static async enrichWithModifiers(itemInventoryModel, itemObj, manager){
let modifiers = {};
for(let modifierData of itemInventoryModel.related_items_item.related_items_item_modifiers){
if(modifierData.operation !== ModifierConst.OPS.SET){
modifierData.value = Number(modifierData.value);
}
modifierData.target = manager.owner; // Set target to currentPlayer
modifiers[modifierData.id] = new Modifier(modifierData);
}
itemObj.modifiers = modifiers;
}
```
### Critical Timing
- **BEFORE items load**: `currentPlayer.stats` is set (fresh object from database)
- **DURING item creation**: Modifiers get `target = manager.owner = currentPlayer`
- **AFTER items load**: Modifiers have correct reference to `currentPlayer.stats`
## Equipment Flow
### Manual Equip (User Action)
**Entry Point**: User clicks equip button, client sends message, server receives
1. **Message Reception** (`lib/inventory/server/message-actions.js` lines 71-73)
```javascript
if(InventoryConst.ACTIONS.EQUIP === data.act){
return await this.executeEquipAction(playerSchema, data);
}
```
2. **Execute Equip Action** (`lib/inventory/server/message-actions.js` lines 360-373)
```javascript
async executeEquipAction(playerSchema, data){
let item = playerSchema.inventory.manager.items[data.idx];
if(!item.equipped){
this.unEquipPrevious(item.group_id, playerSchema.inventory.manager.items); // Unequip same group
await item.equip(); // Equip new item
return true;
}
await item.unequip(); // If already equipped, unequip
return true;
}
```
3. **Item Equip Method** (`npm-packages/reldens-items/lib/item/type/equipment.js` lines 26-35)
```javascript
async equip(applyMods){
this.equipped = true;
await this.manager.fireEvent(ItemsEvents.EQUIP_ITEM, this);
if(applyMods === false || this.manager.applyModifiersAuto === false){
return false;
}
await this.applyModifiers(); // Apply modifiers automatically
}
```
4. **Apply Modifiers** (`npm-packages/reldens-items/lib/item/type/item-base.js` lines 90-105)
```javascript
async changeModifiers(revert){
await this.manager.fireEvent(ItemsEvents.EQUIP_BEFORE+(revert ? 'Revert': 'Apply')+'Modifiers', this);
let modifiersKeys = Object.keys(this.modifiers);
let methodName = revert ? 'revert' : 'apply';
for(let i of modifiersKeys){
this.modifiers[i][methodName](this.target); // this.target is false, but modifier has its own target
}
return this.manager.fireEvent(ItemsEvents.EQUIP+(revert ? 'Reverted' : 'Applied')+'Modifiers', this);
}
```
5. **Modifier Execute** (`npm-packages/reldens-modifiers/lib/modifier.js` lines 84-108)
```javascript
execute(target, revert = false, useBasePropertyToGetValue = false, applyOnBaseProperty = false){
// If target param is false, use this.target (set to currentPlayer in factory)
if(target){
this.target = target;
}
let newValue = this.getModifiedValue(revert, useBasePropertyToGetValue);
let applyToProp = applyOnBaseProperty ? this.basePropertyKey : this.propertyKey;
this.setOwnerProperty(applyToProp, newValue); // Sets currentPlayer.stats.atk
this.state = revert ? ModifierConst.MOD_REVERTED : ModifierConst.MOD_APPLIED;
return true;
}
```
6. **Property Manager Sets Value** (`npm-packages/reldens-modifiers/lib/property-manager.js` lines 22-34)
```javascript
manageOwnerProperty(propertyOwner, propertyString, value){
let propertyPathParts = propertyString.split('/'); // ['stats', 'atk']
let childPropertyOwner = this.extractChildPropertyOwner(propertyOwner, propertyPathParts); // Get stats object
let propertyKey = propertyPathParts[propertyPathParts.length-1]; // 'atk'
if('undefined' === typeof value && !sc.hasOwn(childPropertyOwner, propertyKey)){
ErrorManager.error('Invalid property "'+propertyKey+'" from path: "'+propertyPathParts.join('/')+'"].');
}
if('undefined' !== typeof value){
childPropertyOwner[propertyKey] = value; // Sets stats.atk = newValue
}
return childPropertyOwner[propertyKey];
}
```
7. **Stats Persistence** (`lib/inventory/server/storage-observer.js` lines 68-79)
```javascript
this.manager.listenEvent(
ItemsEvents.EQUIP+'AppliedModifiers',
this.updateAppliedModifiers.bind(this),
...
);
async updateAppliedModifiers(item){
return await this.modelsManager.onChangedModifiers(item, ModifierConst.MOD_APPLIED);
}
```
8. **Persist Data** (`lib/inventory/server/models-manager.js` lines 127-131)
```javascript
async onChangedModifiers(item, action){
return await item.manager.owner.persistData({act: action, item: item});
}
```
9. **Save Player Stats** (`lib/rooms/server/scene.js` lines 228-234)
```javascript
currentPlayer.persistData = async (params) => {
await this.savePlayedTime(currentPlayer);
await this.savePlayerState(currentPlayer.sessionId);
await this.savePlayerStats(currentPlayer, client); // Saves stats to database
};
```
10. **Client Update** (`lib/rooms/server/scene.js` lines 759-763)
```javascript
client.send('*', {
act: GameConst.PLAYER_STATS,
stats: playerSchema.stats,
statsBase: playerSchema.statsBase
});
```
## Modifier Operations
From `@reldens/modifiers/lib/constants.js`:
**1. INC - Increase (flat)**
- Apply: `value + operand`
- Revert: `value - operand`
**2. DEC - Decrease**
- Apply: `value - operand`
- Revert: `value + operand`
**3. DIV - Divide**
- Apply: `value / operand`
- Revert: `value * operand`
**4. MUL - Multiply**
- Apply: `value * operand`
- Revert: `value / operand`
**5. INC_P - Increase by %**
- Apply: `value + (value * operand / 100)`
- Revert: Complex percentage revert
**6. DEC_P - Decrease by %**
- Apply: `value - (value * operand / 100)`
- Revert: Complex percentage revert
**7. SET - Set value**
- Apply: `operand`
- Revert: `false`
**8. METHOD - Custom method**
- Apply: Calls custom method on modifier
- Revert: Calls custom method
**9. SET_N - Set (alt)**
- Apply: `operand`
- Revert: `false`
### INC_P (Increase Percentage) Calculation
From `@reldens/modifiers/lib/calculator.js` lines 30-37:
**Apply**:
```javascript
return originalValue + Math.round(originalValue * operationValue / 100);
```
Example: atk=100, value=5 results in 100 + Math.round(100 * 5 / 100) = 100 + 5 = 105
**Revert**:
```javascript
let revertValue = Math.ceil(originalValue - (originalValue / (100 - operationValue)) * 100);
return originalValue + revertValue;
```
Example: atk=105, value=5 results in Math.ceil(105 - (105/95)*100) = Math.ceil(-5.26) = -5 then 105 + (-5) = 100
## Database Schema
### items_item (Item Definitions)
```sql
CREATE TABLE `items_item` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`key` varchar(255) NOT NULL,
`type` int(11) NOT NULL,
`group_id` int(10) unsigned DEFAULT NULL,
`label` varchar(255) DEFAULT NULL,
`description` text,
`qty_limit` int(11) DEFAULT NULL,
`uses_limit` int(11) DEFAULT NULL,
`useTimeOut` int(11) DEFAULT NULL,
`execTimeOut` int(11) DEFAULT NULL,
`customData` text,
PRIMARY KEY (`id`)
);
```
### items_item_modifiers (Item Modifier Definitions)
```sql
CREATE TABLE `items_item_modifiers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`item_id` int(10) unsigned NOT NULL,
`key` varchar(255) NOT NULL,
`property_key` varchar(255) NOT NULL,
`operation` int(11) NOT NULL,
`value` varchar(255) NOT NULL,
`maxProperty` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`)
);
```
- `item_id`: References the item this modifier belongs to
- `key`: Modifier identifier (e.g., 'atk')
- `property_key`: Path to property to modify (e.g., 'stats/atk')
- `operation`: Operation ID (1-9, see Modifier Operations table)
- `value`: Value to apply (as string, converted to number if not SET operation)
- `maxProperty`: Optional max value property path (e.g., 'statsBase/hp')
### items_inventory (Player Item Instances)
```sql
CREATE TABLE `items_inventory` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`owner_id` int(10) unsigned NOT NULL,
`item_id` int(10) unsigned NOT NULL,
`qty` int(11) NOT NULL,
`remaining_uses` int(11) DEFAULT NULL,
`is_active` tinyint(1) DEFAULT 0,
PRIMARY KEY (`id`),
FOREIGN KEY (`owner_id`) REFERENCES `players` (`id`),
FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`)
);
```
- `owner_id`: Player ID who owns this item instance
- `item_id`: References the item definition
- `qty`: Quantity (-1 for unlimited)
- `remaining_uses`: Uses left (if item has uses limit)
- `is_active`: 1 if equipped, 0 if not (for equipment items only)
## Event Flow
### Equipment Events Sequence
1. `ItemsEvents.EQUIP_ITEM` - Fired when equip() starts
- **Listener**: `StorageObserver.saveEquippedItemAsActive()` - Updates `is_active=1` in database
2. `ItemsEvents.EQUIP_BEFORE+'Apply'+'Modifiers'` - Before modifiers are applied
- No default listeners
3. `ItemsEvents.EQUIP+'Applied'+'Modifiers'` - After modifiers are applied
- **Listener**: `StorageObserver.updateAppliedModifiers()` - Calls `persistData()` to save stats
4. `reldens.playerPersistDataBefore` - Before data persistence
- Custom hooks can intercept here
5. `reldens.savePlayerStatsUpdateClient` - After stats saved, before client update
- **Listener**: `UsersPlugin.updateClientsWithPlayerStats()` - Updates life bar UI
6. Client receives `GameConst.PLAYER_STATS` message with updated stats
### Unequip Events Sequence
1. `ItemsEvents.UNEQUIP_ITEM` - Fired when unequip() starts
- **Listener**: `StorageObserver.saveUnequippedItemAsInactive()` - Updates `is_active=0` in database
2. `ItemsEvents.EQUIP_BEFORE+'Revert'+'Modifiers'` - Before modifiers are reverted
- No default listeners
3. `ItemsEvents.EQUIP+'Reverted'+'Modifiers'` - After modifiers are reverted
- **Listener**: `StorageObserver.updateRevertedModifiers()` - Calls `persistData()` to save stats
4-6. Same persistence and client update flow as equip
## Testing Checklist
- Equip item - Stats increase correctly
- Unequip item - Stats revert to base value
- Logout with equipped item - Stats saved correctly
- Login with equipped item - Stats loaded with modifiers applied
- Unequip after login - Stats revert to base value correctly
- Multiple items in same group - Only one equipped at a time
- Percentage modifiers - Calculate correctly for different base values
- Flat modifiers - Add/subtract exact values
- Max/min property limits - Respect statsBase maximums
## Performance Considerations
- Modifiers are applied synchronously in a loop (item-base.js line 101-103)
- For items with many modifiers, this could cause brief delay
- Stats are saved to database after every equip/unequip operation
- Consider batching stats updates if players frequently swap equipment
## Extension Points
### Custom Item Types
Create custom item class extending ItemBase or Equipment:
```javascript
const Equipment = require('@reldens/items-system').ItemBase;
class MagicWeapon extends Equipment {
async equip(applyMods){
// Custom equip logic
await super.equip(applyMods);
// Post-equip custom logic
}
}
```
Register in `server/customClasses/inventory/items`:
```javascript
itemClasses: {
'magic_sword': MagicWeapon
}
```
### Custom Modifiers
Create custom modifier with METHOD operation:
```javascript
const { Modifier } = require('@reldens/modifiers');
class CustomModifier extends Modifier {
customCalculation(modifier, propertyValue){
// Your custom logic
return newValue;
}
}
```
Set in database:
```sql
INSERT INTO items_item_modifiers VALUES (
NULL, item_id, 'custom', 'stats/custom', 8, 'customCalculation', NULL
);
```
### Event Hooks
Hook into any event for custom logic:
```javascript
events.on('reldens.createdPlayerSchema', async (client, userModel, currentPlayer, room) => {
// Custom logic when player is created
});
inventoryServer.manager.listenEvent(ItemsEvents.EQUIP_ITEM, async (item) => {
// Custom logic when any item is equipped
});
```
## References
- `@reldens/items-system` package: D:\dap\work\reldens\npm-packages\reldens-items
- `@reldens/modifiers` package: D:\dap\work\reldens\npm-packages\reldens-modifiers
- Sample data: D:\dap\work\reldens\src\migrations\production\reldens-sample-data-v4.0.0.sql
================================================
FILE: .claude/player-state-flow.md
================================================
# Player State Flow - Complete Technical Guide
## Overview
This document explains the complete player state management system in Reldens, including the database entity refactor that introduced the "related_" naming convention, and how player state flows from database to runtime.
---
## Architecture Layers
### 1. Database Layer (Persistent Storage)
After the entity refactor, all database relations use the **"related_" prefix** (this is the NEW/CURRENT convention, NOT legacy):
```javascript
UsersModel {
id: number,
email: string,
username: string,
password: string,
role_id: number,
// NEW: Database relations with "related_" prefix
related_users_login: UsersLoginModel[],
related_players: PlayersModel[] // ← Array of all players for this user
}
PlayersModel {
id: number,
user_id: number,
name: string,
created_at: Date,
updated_at: Date,
// NEW: Player state from database (persistent)
related_players_state: PlayersStateModel {
id: number,
player_id: number,
room_id: number, // ← Last SAVED room
x: number, // ← Last SAVED position
y: number,
dir: string
// NOTE: NO scene property in database model!
}
}
```
**Key Points:**
- `related_players` is an **array** (users can have multiple characters)
- `related_players_state` is the **database snapshot** of player position
- Database model does NOT include `scene` property (only `room_id`)
---
### 2. Runtime Layer (In-Memory During Gameplay)
During login and gameplay, additional properties are added for runtime state management:
```javascript
// After login processing:
userModel {
...database fields,
related_players: PlayersModel[], // From database
// ADDED AT RUNTIME: Selected player reference
player: PlayersModel { // ← Selected from related_players[]
...database fields,
related_players_state: { ... }, // Database snapshot
// ADDED AT RUNTIME: Enhanced runtime state
state: {
room_id: number, // ← CURRENT room (updated during gameplay)
x: number, // ← CURRENT position
y: number,
dir: string,
scene: string // ← ADDED: Room name (not in database!)
}
}
}
```
**Key Points:**
- `userModel.player` is **assigned at runtime** from `related_players[]`
- `player.state` is **created during login** and updated during gameplay
- `player.state.scene` is **added by server**, not from database
- `related_players_state` remains **unchanged** after initial load (becomes stale)
---
## Complete Login Flow
### Step 1: User Authentication
**File:** `lib/rooms/server/login.js:70-107` (onAuth)
```javascript
async onAuth(client, options, request) {
// Load user from database
let loginResult = await this.loginManager.processUserRequest(options);
// Select player if specified
if(sc.hasOwn(options, 'selectedPlayer')){
loginResult.user.player = this.getPlayerByIdFromArray(
loginResult.user.related_players, // ← From database array
options.selectedPlayer
);
}
return loginResult.user; // ← Becomes userModel in onJoin
}
```
### Step 2: Load User From Database
**File:** `lib/users/server/manager.js:67-83`
```javascript
async loadUserByUsername(username) {
let loadedUser = await this.usersRepository.loadOneByWithRelations(
'username',
username,
['related_users_login', 'related_players.related_players_state']
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Loads players WITH their state from DB
);
return loadedUser;
}
```
**Result:** User loaded with `related_players[]` array, each player has `related_players_state` from database.
### Step 3: Map Player State Relation
**File:** `lib/game/server/login-manager.js:351-361`
```javascript
mapPlayerStateRelation(user) {
if(!sc.isArray(user.related_players)){
return;
}
for(let player of user.related_players){
if(player.related_players_state && !player.state){
// Create runtime state from database state
player.state = player.related_players_state;
}
}
}
```
**CRITICAL:** This creates `player.state` by assigning `player.related_players_state`.
**Question:** Is this assignment by reference or copy?
- In JavaScript, object assignment is **by reference**
- BUT: Database ORM models might be immutable/frozen
- **Result:** They can diverge during gameplay
### Step 4: Set Scene On Players
**File:** `lib/game/server/login-manager.js:423-441`
```javascript
async setSceneOnPlayers(user, userData) {
for(let player of user.related_players){
if(!player.state){
continue;
}
// Check if user selected a different scene on login
let config = this.config.get('client/rooms/selection');
if(config.allowOnLogin && userData['selectedScene'] &&
userData['selectedScene'] !== RoomsConst.ROOM_LAST_LOCATION_KEY){
await this.applySelectedLocation(player, userData['selectedScene']);
}
// CRITICAL: Add scene property to state
player.state.scene = await this.getRoomNameById(player.state.room_id);
// ^^^^^ ADDED HERE - not in database!
}
}
```
**Result:** Each player now has `player.state.scene` with the room name string.
### Step 5: Select Player (Runtime Assignment)
**File:** `lib/rooms/server/login.js:89-91`
```javascript
if(sc.hasOwn(options, 'selectedPlayer')){
loginResult.user.player = this.getPlayerByIdFromArray(
loginResult.user.related_players,
options.selectedPlayer
);
}
```
**Result:** `userModel.player` now references ONE player from the array with both:
- `player.related_players_state` (database snapshot)
- `player.state` (runtime state with scene)
---
## Gameplay Flow
### Joining Scene Room
**File:** `lib/rooms/server/scene.js:126-156`
```javascript
async onJoin(client, options, userModel) {
// userModel already has player selected from onAuth
// Validate using RUNTIME state (not database state!)
if(this.validateRoomData){
if(!userModel.player.state){ // ← Check runtime state exists
Logger.warning('Missing user player state.', userModel);
return false;
}
if(!this.validateRoom(userModel.player.state.scene, isGuest)){
// ^^^^^ Use runtime state with scene!
return false;
}
}
// Create player schema in room...
}
```
**FIX APPLIED:** Changed from `related_players_state.scene` (doesn't exist) to `state.scene` (exists).
### Saving Player State During Gameplay
**File:** `lib/rooms/server/scene.js:708-737`
```javascript
async savePlayerState(sessionId) {
let playerSchema = this.playerBySessionIdFromState(sessionId);
// Extract CURRENT position from runtime state
let {room_id, x, y, dir} = playerSchema.state; // ← From state, NOT related_players_state
let playerId = playerSchema.player_id;
let updatePatch = {room_id, x: parseInt(x), y: parseInt(y), dir};
// Update database with CURRENT position
updateResult = await this.loginManager.usersManager.updateUserStateByPlayerId(
playerId,
updatePatch
);
return playerSchema;
}
```
**Key Points:**
- Database updated FROM `playerSchema.state` (runtime)
- Database updated TO `players_state` table (will become `related_players_state` on next login)
- `related_players_state` in current session is NEVER updated (remains stale)
---
## Data Flow Diagram
**Step 1: DATABASE (players_state table)**
- room_id: 4, x: 400, y: 345, dir: 'down'
- (NO scene property)
**Step 2: LOAD - UsersManager.loadUserByUsername()**
- related_players[].related_players_state = database snapshot
**Step 3: MAP - LoginManager.mapPlayerStateRelation()**
- player.state = player.related_players_state
- (Assignment creates runtime state)
**Step 4: ENHANCE - LoginManager.setSceneOnPlayers()**
- player.state.scene = getRoomNameById(player.state.room_id)
- (Adds scene property to runtime state)
**Step 5: SELECT - RoomLogin.onAuth()**
- userModel.player = getPlayerByIdFromArray(...)
- (Assigns selected player to userModel.player)
**Step 6: VALIDATE - RoomScene.onJoin()**
- Check: userModel.player.state exists
- Validate: userModel.player.state.scene matches room
**Step 7: GAMEPLAY - Player moves, changes scenes**
- Updates: playerSchema.state (runtime)
- Unchanged: player.related_players_state (stale)
**Step 8: SAVE - RoomScene.savePlayerState()**
- Read FROM: playerSchema.state (current position)
- Write TO: database players_state table
- (Becomes related_players_state on next login)
---
## State Divergence
After login, you have **TWO sources of state** that can diverge:
### Example Session:
**Initial Login:**
```javascript
userModel.player.related_players_state = {
room_id: 4, // Town (from database)
x: 400,
y: 345,
dir: 'down'
}
userModel.player.state = {
room_id: 4, // Same as database
x: 400,
y: 345,
dir: 'down',
scene: 'reldens-town' // Added by server
}
```
**After Scene Change (player moves to house):**
```javascript
userModel.player.related_players_state = {
room_id: 4, // UNCHANGED (stale)
x: 400,
y: 345,
dir: 'down'
}
userModel.player.state = {
room_id: 2, // UPDATED to house
x: 548,
y: 615,
dir: 'up',
scene: 'reldens-house-1' // UPDATED
}
```
**On Logout:** `state` is saved to database, becomes `related_players_state` on next login.
---
## Key Takeaways
1. **"related_" prefix is the NEW database relation naming** (not legacy)
2. **`related_players_state`** = Database snapshot (stale after load, no scene property)
3. **`state`** = Runtime state (active, has scene property, source of truth for gameplay)
4. **`scene` property** = Only exists in runtime `state`, NOT in database model
5. **Validation must use** `player.state.scene`, NOT `player.related_players_state.scene`
6. **Database updates** read from `state` and write to `players_state` table
7. **`related_players_state` is never updated** during a session (snapshot only)
---
## Code References
**Key Files:**
- `lib/users/server/manager.js:67-83` - Load user with relations
- `lib/game/server/login-manager.js:351-361` - Map player state relation
- `lib/game/server/login-manager.js:423-441` - Set scene on players
- `lib/rooms/server/login.js:70-107` - Authentication and player selection
- `lib/rooms/server/scene.js:126-156` - Scene validation
- `lib/rooms/server/scene.js:708-737` - Save player state
**Database Tables:**
- `users` - User accounts
- `players` - Player characters
- `players_state` - Player positions (becomes `related_players_state` when loaded)
**Entity Relations:**
- `UsersModel.related_players` relates to `PlayersModel[]`
- `PlayersModel.related_players_state` relates to `PlayersStateModel`
================================================
FILE: .claude/room-data-optimization.md
================================================
# Room Data Optimization - Scene Data Filter
**Purpose**: Optimize Colyseus schema buffer usage by detecting and extracting shared properties from room objects, reducing data transmission size without losing functionality.
---
## Overview
The SceneDataFilter system prevents Colyseus buffer overflow by analyzing room data and extracting identical properties across multiple objects into a shared defaults structure. This reduces buffer usage from ~176 KB to under 64 KB for rooms with 400+ objects.
**Key Components**:
- **Server**: `SceneDataFilter` (`lib/rooms/server/scene-data-filter.js`) - Detects shared properties, creates optimized data structure
- **Client**: `AnimationsDefaultsMerger` (`lib/game/client/animations-defaults-merger.js`) - Merges defaults back into objects
**Critical Design Principle**: The filter NEVER adds properties to objects. It ONLY extracts existing identical properties to a separate defaults structure.
---
## Server-Side: SceneDataFilter
### Architecture
**SceneDataFilter Methods:**
- filterRoomData() - Main entry (called by State.mapRoomData)
- buildCompleteData() - Returns unfiltered data (sendAll: true)
- buildFilteredData() - Returns optimized data (sendAll: false)
- optimizeData() - Generic optimization method
- detectIdenticalProperties() - Finds shared properties across objects
- valuesAreDifferent() - Compares values for optimization
### How It Works
1. **No Hardcoded Fields**: Filter dynamically detects which fields are identical across objects
2. **Grouping**: Objects are grouped by a shared field for comparison
- `preloadAssets`: Groups by `asset_type` (filters `asset_type === 'spritesheet'`)
- `objectsAnimationsData`: Groups by `asset_key` field, falls back to `key` field if `asset_key` not present
3. **Detection**: For each group with 2+ objects, detects properties with identical values across ALL objects
4. **Extraction**: Identical properties extracted to defaults object, keyed by grouping field value
5. **Grouping Field Preservation**: The grouping field (e.g., `asset_key`) is removed from defaults and kept in each object so client can look up defaults
### Optimization Logic
```javascript
// Example: 200 objects with asset_key: 'enemy_forest_1'
{
'enemy_1': {asset_key: 'enemy_forest_1', type: 'npc', enabled: true, x: 100, y: 200},
'enemy_2': {asset_key: 'enemy_forest_1', type: 'npc', enabled: true, x: 150, y: 250},
...
}
// Filter detects: type, enabled are identical across all 200 objects
// Result:
{
objectsAnimationsData: {
'enemy_1': {asset_key: 'enemy_forest_1', x: 100, y: 200},
'enemy_2': {asset_key: 'enemy_forest_1', x: 150, y: 250},
...
},
animationsDefaults: {
'enemy_forest_1': {type: 'npc', enabled: true, ...}
}
}
```
**Key Points**:
- `asset_key` stays in each object (needed for client to lookup defaults)
- Only properties with IDENTICAL values across ALL objects in group are extracted
- Properties with different values (x, y, content, options, id) stay in each object
- Single-object groups are NOT optimized (no shared properties to extract)
### When Optimization Happens vs Doesn't
**Town Room (6 NPCs)**:
- Each NPC has unique properties (different types, content, options)
- No groups with 2+ identical objects
- Result: `animationsDefaults: {}` (empty), all data stays in objects
- All objects keep their original structure with `key` field as asset reference
**Forest Room (400 NPCs)**:
- 200 enemies of type A, 200 enemies of type B
- Each group has identical shared properties
- Result: `animationsDefaults: {'enemy_forest_1': {...}, 'enemy_forest_2': {...}}`
- Optimized objects have `asset_key` field added by filter for grouping
- Optimized objects contain only unique properties (x, y) + `asset_key` reference
### preloadAssets Filtering
**Process**:
1. Filters only `asset_type === 'spritesheet'` (matches client loader)
2. Groups remaining assets by `asset_type`
3. Detects identical properties across assets with same type
4. Extracts to `preloadAssetsDefaults[asset_type]`
**Typically Kept Fields** (detected dynamically, NOT hardcoded):
- `asset_type` - Grouping field (stays in each asset)
- `asset_key` - Usually unique per asset
- `asset_file` - Usually unique per asset
- `extra_params` - Often identical for same asset_type
**Typically Removed to Defaults** (if identical across assets):
- Database metadata fields if they happen to be identical
**Result**: Minimal optimization for preloadAssets since most fields are unique per asset.
### objectsAnimationsData Optimization
**Process**:
1. Groups objects by `asset_key` field (or `key` field if no `asset_key`)
2. For groups with 2+ objects: Detects identical properties
3. Removes grouping field from identical properties (keeps in each object)
4. Extracts identical properties to `animationsDefaults[grouping_value]`
5. Objects retain only unique properties + grouping field reference
**Grouping Field Priority**:
1. Use `asset_key` if present (already set by server for optimized objects)
2. Fall back to `key` field if no `asset_key` (non-optimized objects)
**Critical**: Grouping field (`asset_key` or `key`) is NEVER extracted to defaults. It must stay in each object so client can look up the correct defaults entry.
---
## Client-Side: AnimationsDefaultsMerger
### Purpose
Merges extracted defaults back into objects after receiving optimized data from server.
### When It Runs
```javascript
// Only runs if roomData has animationsDefaults property
if(sc.hasOwn(roomData, 'animationsDefaults')){
AnimationsDefaultsMerger.mergeDefaults(roomData);
}
```
**Important**: Server adds `animationsDefaults: {}` (even if empty) when filter is active. This triggers the merger to run.
### Merge Logic
```javascript
for(let key of objectKeys){
let objectData = objectsAnimationsData[key];
// Only process objects with asset_key from server (optimized objects)
if(!sc.hasOwn(objectData, 'asset_key')){
continue; // Keep non-optimized objects untouched
}
// Set key to map index for optimized objects
objectData.key = key;
// Lookup and merge defaults
let assetKey = objectData.asset_key;
if(sc.hasOwn(animationsDefaults, assetKey)){
let defaults = animationsDefaults[assetKey];
objectsAnimationsData[key] = Object.assign({}, defaults, objectData);
}
}
```
### Key Behavior
**Optimized Objects** (have `asset_key` from server):
1. `objectData.key` set to map index (e.g., 'enemy_1')
2. Defaults looked up using `asset_key` value
3. Merged: `Object.assign({}, defaults, objectData)` - object properties override defaults
4. Result has all properties needed for rendering
**Non-Optimized Objects** (no `asset_key` from server):
1. Skipped entirely - no modifications
2. `objectData.key` keeps original value (asset reference like 'people_town_1')
3. All original properties preserved as-is
4. Ready for rendering without merge
### Why This Matters
The merger MUST check for `asset_key` presence before modifying objects because:
- **Objects without `asset_key`**: Were NOT optimized by server, have complete data, use `key` field as asset reference
- **Objects with `asset_key`**: Were optimized by server, have partial data, need defaults merged, use `asset_key` as asset reference
If merger modifies non-optimized objects (changes their `key` field), it breaks asset loading and dialog functionality.
---
## Data Flow Examples
### Town Room (No Optimization)
**Server Processing**:
```javascript
// Original data
objectsAnimationsData: {
'ground-collisions444': {
key: 'door_house_1',
type: 'anim',
enabled: true,
x: 400,
y: 310,
...all properties...
},
'house-collisions-over-player535': {
key: 'people_town_1',
type: 'npc',
enabled: true,
content: 'Hello! My name is Alfred...',
x: 240,
y: 368,
...all properties...
}
}
// SceneDataFilter analysis:
// - Group by 'key' field (no asset_key present)
// - Each object has unique 'key' value = single-object groups
// - No optimization performed
// Server output
{
objectsAnimationsData: { ...unchanged... },
animationsDefaults: {} // Empty - triggers merger but no data to merge
}
```
**Client Processing**:
```javascript
// AnimationsDefaultsMerger.mergeDefaults() runs
for(let key of ['ground-collisions444', 'house-collisions-over-player535']){
let objectData = objectsAnimationsData[key];
// Check for asset_key
if(!sc.hasOwn(objectData, 'asset_key')){
continue; // SKIP - no modifications, keep original data
}
}
// Result: All objects unchanged
objectsAnimationsData: {
'ground-collisions444': {key: 'door_house_1', ...},
'house-collisions-over-player535': {key: 'people_town_1', ...}
}
// AnimationEngine uses props.key fallback
// object['ground-collisions444'].key = 'door_house_1' loads asset 'door_house_1'
// object['house-collisions-over-player535'].key = 'people_town_1' NPC dialog works
```
### Forest Room (With Optimization)
**Server Processing**:
```javascript
// Original data: 400 objects, 200 identical enemies per type
objectsAnimationsData: {
'enemy_1': {
asset_key: 'enemy_forest_1', // Already set by server
type: 'npc',
enabled: true,
targetName: 'enemy-pve',
layerName: 'enemies-layer',
x: 100,
y: 200
},
'enemy_2': {
asset_key: 'enemy_forest_1',
type: 'npc',
enabled: true,
targetName: 'enemy-pve',
layerName: 'enemies-layer',
x: 150,
y: 250
},
// ... 198 more with same asset_key
}
// SceneDataFilter analysis:
// - Group by 'asset_key' field
// - 'enemy_forest_1' group has 200 objects
// - Detects identical: type, enabled, targetName, layerName
// - Keeps unique: x, y (different per object)
// - Keeps grouping field: asset_key (needed for lookup)
// Server output
{
objectsAnimationsData: {
'enemy_1': {asset_key: 'enemy_forest_1', x: 100, y: 200},
'enemy_2': {asset_key: 'enemy_forest_1', x: 150, y: 250},
// ... 198 more (only unique props + asset_key)
},
animationsDefaults: {
'enemy_forest_1': {
type: 'npc',
enabled: true,
targetName: 'enemy-pve',
layerName: 'enemies-layer',
// ... all shared properties
}
}
}
```
**Client Processing**:
```javascript
// AnimationsDefaultsMerger.mergeDefaults() runs
for(let key of ['enemy_1', 'enemy_2', ...]){
let objectData = objectsAnimationsData[key];
// {asset_key: 'enemy_forest_1', x: 100, y: 200}
// Check for asset_key
if(!sc.hasOwn(objectData, 'asset_key')){
continue; // NOT executed - asset_key exists
}
// Set key to map index
objectData.key = key; // 'enemy_1'
// Lookup defaults
let assetKey = objectData.asset_key; // 'enemy_forest_1'
let defaults = animationsDefaults['enemy_forest_1'];
// Merge
objectsAnimationsData[key] = Object.assign({}, defaults, objectData);
// Result: {
// type: 'npc',
// enabled: true,
// targetName: 'enemy-pve',
// layerName: 'enemies-layer',
// asset_key: 'enemy_forest_1',
// key: 'enemy_1',
// x: 100,
// y: 200
// }
}
// AnimationEngine uses props.asset_key (exists) loads asset 'enemy_forest_1'
// All properties restored from defaults + unique props
```
---
## Performance Impact
### 400 Objects Example (Forest Room)
**Unfiltered**:
- preloadAssets: 400 × 2 assets × 275 bytes = 220 KB
- objectsAnimationsData: 400 × 150 bytes = 60 KB
- roomData: 10 KB
- **Total sceneData**: ~290 KB
- **Total State (with 50 players)**: ~176 KB encoded
- **Buffer overflow**: Required 176 KB vs 8 KB default
**Optimized**:
- preloadAssets: 2 spritesheets × 95 bytes = 0.2 KB
- objectsAnimationsData: 400 × 35 bytes = 14 KB
- animationsDefaults: 2 entries × 140 bytes = 0.3 KB
- roomData: 10 KB
- **Total sceneData**: ~25 KB
- **Total State (with 50 players)**: ~80 KB encoded
**Reduction**:
- sceneData: 265 KB saved (91% reduction)
- Total State: 96 KB saved (54.5% reduction)
---
## Configuration
### sendAll Flag
**Path**: `server/rooms/data/sendAll`
**Default**: `false` (filtering enabled)
```sql
INSERT INTO config (path, value, scope) VALUES
('server/rooms/data/sendAll', 'false', 'server');
```
**Values**:
- `false`: Enables optimization (recommended for production)
- `true`: Sends all data unfiltered (debugging only)
### Custom Processor
For custom filtering logic, define a processor in server plugin.
**Path**: `server/customClasses/sceneDataProcessor`
**Method**: `process({ roomData, filter })`
**Example**:
```javascript
class CustomSceneDataProcessor {
process({ roomData, filter }) {
let customData = Object.assign({}, roomData);
// Use filter methods for standard optimization
let optimized = filter.buildFilteredData(roomData);
// Add custom fields
customData.customField = 'custom value';
return Object.assign({}, optimized, customData);
}
}
config.set('server/customClasses/sceneDataProcessor', new CustomSceneDataProcessor());
```
---
## Key Concepts
### asset_key Field
**Purpose**: Reference to shared defaults, used for grouping and lookup
**When Present**:
- Server added it during optimization (object was grouped with others)
- Indicates object has partial data, needs defaults merged
- Client uses it to lookup defaults and as asset reference
**When NOT Present**:
- Object was not optimized (unique properties, single-object group)
- Object has complete data, no merge needed
- Client uses `key` field as asset reference
### key Field
**Two Different Roles**:
1. **Non-Optimized Objects**: Asset reference (e.g., 'people_town_1')
- Original value from server
- AnimationEngine fallback: `sc.get(props, 'asset_key', props.key)`
- Used to load sprite asset
2. **Optimized Objects**: Map index (e.g., 'enemy_1')
- Set by client merger to map key
- Not used for asset loading (asset_key used instead)
- Identifies object instance
### Grouping Fields
**Purpose**: Field used to group objects for comparison and defaults lookup
**Requirements**:
- Must be identical across all objects in group
- Must stay in each object (NOT extracted to defaults)
- Client needs it to look up correct defaults entry
**Examples**:
- `asset_type` for preloadAssets
- `asset_key` for objectsAnimationsData (if present)
- `key` for objectsAnimationsData (fallback if no asset_key)
### Why Grouping Field Must Stay in Objects
```javascript
// If asset_key was extracted to defaults:
objectsAnimationsData: {
'enemy_1': {x: 100, y: 200} // No asset_key!
}
animationsDefaults: {
'enemy_forest_1': {asset_key: 'enemy_forest_1', type: 'npc', ...}
}
// Client can't merge - doesn't know which defaults to use!
// No way to know 'enemy_1' should use 'enemy_forest_1' defaults
```
Keeping grouping field in each object allows lookup:
```javascript
let assetKey = objectData.asset_key; // 'enemy_forest_1'
let defaults = animationsDefaults[assetKey]; // Found!
```
---
## Integration Points
### Server Integration
**RoomScene** (`lib/rooms/server/scene.js`):
```javascript
this.sceneDataFilter = new SceneDataFilter({configManager: this.configManager});
```
**State** (`lib/rooms/server/state.js`):
```javascript
constructor(data){
this.sceneDataFilter = sc.get(data, 'sceneDataFilter', false);
}
mapRoomData(roomData){
if(false === this.sceneDataFilter){
return roomData;
}
return this.sceneDataFilter.filterRoomData(roomData);
}
```
### Client Integration
**RoomEvents** (`lib/game/client/room-events.js`):
```javascript
this.room.onMessage('*', (message) => {
if('sceneData' === message.act){
let roomData = message.scene;
// Merge defaults if present
if(sc.hasOwn(roomData, 'animationsDefaults')){
AnimationsDefaultsMerger.mergeDefaults(roomData);
}
// Process room data...
}
});
```
**AnimationEngine** (`lib/game/client/animation-engine.js`):
```javascript
constructor(props){
// Uses asset_key if present, falls back to key
this.asset_key = sc.get(props, 'asset_key', props.key);
}
```
---
## Testing
### Verify Optimization Behavior
**Town Room (No Optimization Expected)**:
1. Join Town room
2. Check browser console: No `asset_key` in objects
3. Verify: `animationsDefaults: {}`
4. Test NPC dialogs work correctly
**Forest Room (Optimization Expected)**:
1. Join Forest room with 400 objects
2. Check browser console: Objects have `asset_key` field
3. Verify: `animationsDefaults` has entries
4. Test enemies render and behave correctly
### Measure Data Size
Add logging in `State.mapRoomData()`:
```javascript
this.sceneData = JSON.stringify(roomData);
Logger.info('sceneData size: ' + this.sceneData.length + ' bytes');
```
### Verify Buffer Overflow Resolved
```bash
npm run bots -- --room=reldens-bots-forest --bots=50
```
Expected: No buffer overflow warnings in server console.
### Verify Client Functionality
1. Join rooms with various object counts
2. Verify spritesheets load correctly
3. Verify animations play correctly
4. Verify NPC dialogs work correctly
5. Check browser console for errors related to missing assets or properties
---
## Debugging
### Disable Filtering
Set in database or config:
```javascript
config.set('server/rooms/data/sendAll', true);
```
Sends all database fields to client for debugging. Compare filtered vs unfiltered data to identify issues.
### Log Optimization Results
```javascript
class DebugSceneDataProcessor {
process({ roomData, filter }) {
let filtered = filter.buildFilteredData(roomData);
Logger.info('objectsAnimationsData count:', Object.keys(filtered.objectsAnimationsData || {}).length);
Logger.info('animationsDefaults entries:', Object.keys(filtered.animationsDefaults || {}).length);
// Log which objects were optimized
for(let key in filtered.objectsAnimationsData){
let obj = filtered.objectsAnimationsData[key];
if(sc.hasOwn(obj, 'asset_key')){
Logger.info('Optimized object:', key, 'asset_key:', obj.asset_key);
}
}
return filtered;
}
}
```
### Common Issues
**NPCs Not Visible**:
- Check browser console for asset loading errors
- Verify `asset_key` or `key` field present in object
- Verify asset exists in preloadAssets
- Check AnimationEngine.asset_key is set correctly
**NPC Dialogs Not Working**:
- Verify non-optimized objects keep original `key` field value
- Check AnimationsDefaultsMerger is NOT modifying objects without `asset_key`
- Verify dialog system uses correct object reference
**Buffer Overflow Still Occurring**:
- Verify `sendAll: false` in config
- Check optimization is detecting shared properties
- Log data size before/after filtering
- Verify client is merging defaults correctly
---
## References
- **Server Filter**: `lib/rooms/server/scene-data-filter.js`
- **Client Merger**: `lib/game/client/animations-defaults-merger.js`
- **State Integration**: `lib/rooms/server/state.js`
- **Scene Integration**: `lib/rooms/server/scene.js`
- **Room Events**: `lib/game/client/room-events.js`
- **Animation Engine**: `lib/game/client/animation-engine.js`
- **Colyseus Schema**: https://docs.colyseus.io/state/schema/
================================================
FILE: .claude/room-images-tileset-override-flow.md
================================================
# Room Images and Tileset Override System
## Overview
This document explains how the room scene images upload system works and how the `overrideSceneImagesWithMapFile` option automatically synchronizes scene images with the Tiled map file tilesets.
## Configuration
**Config Path:** `server/rooms/maps/overrideSceneImagesWithMapFile`
**Type:** Boolean
**Default:** `true`
**Location:** Database `config` table or environment variable
When enabled, the system uses the Tiled map file as the source of truth for scene images, automatically overriding the `scene_images` field with images listed in the map's tilesets.
## File Locations
### Source Code
- **Validator:** `lib/admin/server/room-map-tilesets-validator.js`
- **Subscriber:** `lib/admin/server/subscribers/rooms-entity-subscriber.js`
- **File Upload Renderer:** `lib/admin/server/rooms-file-upload-renderer.js`
- **Admin Plugin:** `lib/admin/server/plugin.js`
### Admin Interface
- **Tileset File Item Template:** `theme/admin/templates/fields/edit/tileset-file-item.html`
- **Tileset Alert Wrapper Template:** `theme/admin/templates/fields/edit/tileset-alert-wrapper.html`
- **Client JS:** `theme/admin/reldens-admin-client.js`
- **Client CSS:** `theme/admin/reldens-admin-client.css`
- **Router:** `npm-packages/reldens-cms/lib/admin-manager/router-contents.js`
## Database Schema
### Rooms Table
- `id` - Room identifier
- `map_filename` - Tiled map JSON file (e.g., `reldens-forest.json`)
- `scene_images` - Comma-separated list of tileset images (e.g., `reldens-forest.png,reldens-town.png`)
### Upload Configuration
Both fields are configured as upload fields:
- `map_filename` - Single file upload, bucket: `theme/assets/maps`
- `scene_images` - Multiple file upload, bucket: `theme/assets/images`
## System Flow
### 1. Initial Room Creation
**User Actions:**
1. Navigate to Admin → Rooms → Create New
2. Upload map JSON file to `map_filename` field
3. Upload tileset images to `scene_images` field
4. Click Save
**System Processing:**
1. **Upload Phase** - Files saved to respective buckets
2. **Validation Phase** - `validateUploadedFiles()` checks required fields
3. **Save Phase** - Entity created in database
4. **Post-Save Event** - `reldens.adminAfterEntitySave` fires
5. **Validator Execution** - `RoomMapTilesetsValidator.validate()` runs
**Validator Logic:**
```javascript
// Check if override is enabled
overrideEnabled = config.getWithoutLogs('server/rooms/maps/overrideSceneImagesWithMapFile', true)
// Read map file
mapData = readMapFile(bucket, mapFilename, roomId)
// Extract tileset images from map JSON
tilesetImages = extractTilesetImages(mapData.tilesets)
// Example: ['reldens-forest.png']
// Compare with current scene_images
if (tilesetImages !== currentSceneImages) {
// Validate all images exist in scene_images bucket
if (validateImagesExist(tilesetImages, sceneImagesBucket)) {
// Override scene_images with tileset images
roomsRepository.updateById(roomId, {scene_images: tilesetImages.join(',')})
}
}
```
### 2. Room Editing
**User Actions:**
1. Navigate to Admin → Rooms → Edit Room
2. View existing files in both fields
3. Modify files or click Save without changes
**Edit Form Population:**
**Event:** `reldens.adminEditPropertiesPopulation`
**Flow:**
```javascript
// 1. Event emitted with room data
event = {
driverResource, // Entity configuration
renderedEditProperties, // Form properties
loadedEntity, // Room from database
entityId: 'rooms',
entityData: loadedEntity
}
// 2. RoomsEntitySubscriber.populateEditFormTilesetImages() executes
if (overrideSceneImagesWithMapFile) {
// Extract tileset images from map file
tilesetImages = validator.extractTilesetImagesFromEntity(entityData, driverResource)
// Inject into form properties
renderedEditProperties.tilesetImages = tilesetImages
renderedEditProperties.overrideSceneImagesEnabled = true
}
// 3. RoomsFileUploadRenderer processes scene_images field
// Event: reldens.adminBeforeFieldRender
if (propertyKey === 'scene_images' && tilesetImages.length > 0) {
// Render each file with protection flag
for each file:
renderedFileItems.push(render tileset-file-item.html with {
filename,
isProtected: tilesetImages.includes(filename)
})
// Wrap files in alert container
templateData.renderedFiles = render tileset-alert-wrapper.html
}
// 4. Template renders with tileset protection
{{^isProtected}}
X -
{{/isProtected}}
{{filename}}
```
**Result:**
- Protected images (tilesets): NO remove button
- Non-protected images: Remove button shown
- Alert icon displays with info message
### 3. Saving Changes
**Scenario A: No Files Changed**
1. User clicks Save without uploading/removing files
2. Validation passes (existing files satisfy requirement)
3. Entity updated with form data
4. Post-save validator runs
5. If scene_images matches tilesets → No action
6. If mismatch → Override with tileset images
**Scenario B: Add New Image**
1. User uploads additional image to `scene_images`
2. `prepareUploadPatchData()` appends new file to existing files
3. Entity saved with: `existing_images.png,new_image.png`
4. Post-save validator runs
5. Validates tileset images exist
6. **Overrides** scene_images with ONLY tileset images (removes non-tileset images)
**Scenario C: Remove Non-Protected Image**
1. User clicks X button on non-protected image
2. Client adds filename to `removed_scene_images` hidden input
3. `prepareUploadPatchData()` filters removed files
4. Entity saved with filtered list
5. Post-save validator runs
6. Overrides with tileset images (removes non-tileset files)
**Scenario D: Attempt Remove Protected Image (Prevented)**
1. Protected image (tileset) has NO remove button
2. User cannot remove it through UI
3. Alert icon displays: "Images specified in the tileset can't be removed since the option overrideSceneImagesWithMapFile is active."
### 4. Map File Update
**User Updates Map File:**
1. User replaces `map_filename` with new Tiled map
2. New map references different tileset images
3. Entity saved
4. Post-save validator executes
5. Reads new map file tilesets
6. **Replaces** scene_images with new tileset images
7. Old images no longer referenced (user must manage cleanup)
## Technical Details
### Map File Structure
**Example: reldens-forest.json**
```json
{
"tilesets": [
{
"columns": 14,
"firstgid": 1,
"image": "reldens-forest.png",
"imageheight": 408,
"imagewidth": 476,
"name": "reldens-forest",
"tilecount": 168
}
]
}
```
**Extraction Logic:**
```javascript
extractTilesetImages(mapData) {
let tilesets = mapData.tilesets || []
let images = []
for (let tileset of tilesets) {
let tilesetImage = tileset.image // 'reldens-forest.png' or '../images/reldens-forest.png'
let imageFileName = tilesetImage.split('/').pop() // Extract filename only
if (!images.includes(imageFileName)) {
images.push(imageFileName)
}
}
return images // ['reldens-forest.png']
}
```
### Validation Logic
**Array Comparison (validator):**
```javascript
arraysAreEqual(array1, array2) {
if (array1.length !== array2.length) {
return false
}
let sorted1 = [...array1].sort()
let sorted2 = [...array2].sort()
for (let i = 0; i < sorted1.length; i++) {
if (sorted1[i] !== sorted2[i]) {
return false
}
}
return true
}
```
**Image Existence Validation (validator):**
```javascript
validateImagesExist(tilesetImages, sceneImagesBucket, roomId, mapFilename) {
for (let imageFileName of tilesetImages) {
let imageFilePath = FileHandler.joinPaths(sceneImagesBucket, imageFileName)
if (!FileHandler.exists(imageFilePath)) {
return false
}
}
return true
}
```
### Client-Side Protection
**File Item Template (tileset-file-item.html):**
```html
{{^isProtected}}
X -
{{/isProtected}}
{{&filename}}
```
**Alert Wrapper Template (tileset-alert-wrapper.html):**
```html
{{{renderedFileItems}}}
Images specified in the tileset can't be removed since the option overrideSceneImagesWithMapFile is active.
```
**JavaScript Toggle (reldens-admin-client.js):**
```javascript
document.querySelectorAll('.tileset-alert-icon').forEach(icon => {
icon.addEventListener('click', () => {
let message = icon.nextElementSibling
if (message?.classList.contains('tileset-info-message')) {
message.classList.toggle('hidden')
}
})
})
```
## Benefits
1. **Consistency:** Scene images always match map tilesets
2. **Automation:** No manual sync between map and images
3. **Single Source of Truth:** Tiled map file controls image references
4. **Developer Experience:** Edit maps in Tiled, changes auto-sync
## Limitations
1. **One-Way Sync:** Map → Database only (not bidirectional)
2. **Cleanup Required:** Removing tileset from map doesn't delete old image files
3. **Override Always Wins:** Manual changes to scene_images get overwritten on next save
4. **Requires Config:** Must enable `overrideSceneImagesWithMapFile` to activate
## Disabling the Feature
To disable tileset override and manage images manually:
**Option 1: Database Config**
```sql
UPDATE config
SET value = '0'
WHERE path = 'server/rooms/maps/overrideSceneImagesWithMapFile';
```
**Option 2: Environment Variable**
```bash
RELDENS_SERVER_ROOMS_MAPS_OVERRIDESCENEIMAGESWITHMAPFILE=0
```
**Result:**
- Post-save validation skipped
- All images show remove buttons
- Full manual control over scene_images field
- Map file and scene_images can diverge
================================================
FILE: .claude/stat-bars-configuration.md
================================================
# Stat Bars Configuration
## Overview
The player stats bars system is a generic client-side feature that displays visual bars for any configured player stat in the player box UI.
## Configuration Path
**Scope**: `client`
**Path**: `players/barsProperties`
**Type**: JSON (type 4)
## Configuration Structure
The configuration is a JSON object where each key represents a stat key, and each value contains the bar properties for that stat.
```json
{
"statKey": {
"enabled": true,
"label": "Display Label",
"activeColor": "#hexcolor",
"inactiveColor": "#hexcolor"
}
}
```
## Properties
Each stat bar configuration requires the following properties:
- **enabled** (boolean): Whether the bar should be displayed
- **label** (string): The display label shown above the bar
- **activeColor** (string): Hex color for the filled portion of the bar
- **inactiveColor** (string): Hex color for the empty/background portion of the bar
All four properties are required. If any property is missing, the bar will not be displayed.
## Activation Rules
- If config does not exist or is empty: bars system is NOT activated
- If config exists: bars are activated ONLY for stats with all required properties
- Each stat is validated independently via BarProperties model
- Only bars with `ready === true` are rendered
## Database Configuration
### Development Migration
Add to `migrations/development/beta.39.7-sql-update.sql`:
```sql
INSERT INTO `config` (`scope`, `path`, `value`, `type`) VALUES
('client', 'players/barsProperties', '{"hp":{"enabled":true,"label":"HP","activeColor":"#ff0000","inactiveColor":"#330000"},"mp":{"enabled":true,"label":"MP","activeColor":"#0000ff","inactiveColor":"#000033"}}', 4);
```
### Production Migration
Add to `migrations/production/reldens-basic-config-v4.0.0.sql`:
```sql
(92, 'client', 'players/barsProperties', '{"hp":{"enabled":true,"label":"HP","activeColor":"#ff0000","inactiveColor":"#330000"},"mp":{"enabled":true,"label":"MP","activeColor":"#0000ff","inactiveColor":"#000033"}}', 4),
```
## Examples
### HP and MP Bars
```json
{
"hp": {
"enabled": true,
"label": "HP",
"activeColor": "#ff0000",
"inactiveColor": "#330000"
},
"mp": {
"enabled": true,
"label": "MP",
"activeColor": "#0000ff",
"inactiveColor": "#000033"
}
}
```
### Stamina Bar
```json
{
"stamina": {
"enabled": true,
"label": "Stamina",
"activeColor": "#00ff00",
"inactiveColor": "#003300"
}
}
```
### Multiple Stats
```json
{
"hp": {
"enabled": true,
"label": "HP",
"activeColor": "#ff0000",
"inactiveColor": "#330000"
},
"mp": {
"enabled": true,
"label": "MP",
"activeColor": "#0000ff",
"inactiveColor": "#000033"
},
"stamina": {
"enabled": true,
"label": "STA",
"activeColor": "#ffff00",
"inactiveColor": "#333300"
},
"atk": {
"enabled": false,
"label": "ATK",
"activeColor": "#ff6600",
"inactiveColor": "#331100"
}
}
```
In this example, HP, MP, and Stamina bars will be displayed. ATK bar will not be displayed because `enabled: false`.
## Disabling Bars
To disable a specific stat bar, set `enabled: false`:
```json
{
"hp": {
"enabled": false,
"label": "HP",
"activeColor": "#ff0000",
"inactiveColor": "#330000"
}
}
```
To disable the entire bars system, remove the config or set it to an empty object `{}`.
## Technical Notes
- The stat key in the config must match the stat key in the database `stats` table
- Bars are rendered in the order they appear in the configuration object
- Bar values are calculated from `message.stats[statKey]` (current) and `message.statsBase[statKey]` (max)
- Percentage calculation: `(currentValue / maxValue) * 100`
- Bars update automatically when stats change via `reldens.playerStatsUpdateAfter` event
- Bars are rendered inside `#player-stats-bars-wrapper` within `#ui-player-extras` container
---
# Player Names Configuration
## Overview
The player names system displays character names above sprites. Names can be configured separately for the current player and other players.
## Configuration Path
**Scope**: `client`
**Path**: `ui/players`
**Type**: Multiple (boolean, object)
## Configuration Properties
### Visibility Controls
- **showCurrentPlayerName** (type 3 - boolean): Show name for the current player
- Default: `0` (hidden)
- Database: `client/ui/players/showCurrentPlayerName`
- When disabled, current player's name will not be displayed
- Useful when using alternative UI systems or cleaner visual experience
- **showNames** (type 3 - boolean): Show names for all other players
- Default: `1` (enabled)
- Database: `client/ui/players/showNames`
- Controls name visibility for other players (not current player)
- **showNamesLimit** (type 2 - number): Maximum name length before truncation
- Default: `10`
- Database: `client/ui/players/showNamesLimit`
- Names longer than this value will be truncated with '...'
### Visual Appearance
Names are styled using the `nameText` configuration object with the following properties:
- **align** (type 1 - string): Text alignment
- Default: `center`
- Database: `client/ui/players/nameText/align`
- **depth** (type 2 - number): Rendering depth/z-index
- Default: `200000`
- Database: `client/ui/players/nameText/depth`
- **fill** (type 1 - string): Text color
- Default: `#ffffff`
- Database: `client/ui/players/nameText/fill`
- **fontFamily** (type 1 - string): Font family
- Default: `Verdana, Geneva, sans-serif`
- Database: `client/ui/players/nameText/fontFamily`
- **fontSize** (type 1 - string): Font size
- Default: `12px`
- Database: `client/ui/players/nameText/fontSize`
- **height** (type 2 - number): Vertical offset from sprite
- Default: `-90`
- Database: `client/ui/players/nameText/height`
- **shadowBlur** (type 2 - number): Shadow blur radius
- Default: `5`
- Database: `client/ui/players/nameText/shadowBlur`
- **shadowColor** (type 1 - string): Shadow color
- Default: `rgba(0,0,0,0.7)`
- Database: `client/ui/players/nameText/shadowColor`
- **shadowX** (type 2 - number): Shadow X offset
- Default: `5`
- Database: `client/ui/players/nameText/shadowX`
- **shadowY** (type 2 - number): Shadow Y offset
- Default: `5`
- Database: `client/ui/players/nameText/shadowY`
- **stroke** (type 1 - string): Text stroke color
- Default: `#000000`
- Database: `client/ui/players/nameText/stroke`
- **strokeThickness** (type 2 - number): Stroke thickness
- Default: `4`
- Database: `client/ui/players/nameText/strokeThickness`
## Database Configuration
### Development Migration
Add to `migrations/development/[version]-sql-update.sql`:
```sql
INSERT INTO `config` (`scope`, `path`, `value`, `type`) VALUES
('client', 'ui/players/showCurrentPlayerName', '0', 3);
```
### Production Migration
From `migrations/production/reldens-basic-config-v4.0.0.sql`:
Existing configurations (IDs 239-252):
```sql
(239, 'client', 'ui/players/nameText/align', 'center', 1),
(240, 'client', 'ui/players/nameText/depth', '200000', 2),
(241, 'client', 'ui/players/nameText/fill', '#ffffff', 1),
(242, 'client', 'ui/players/nameText/fontFamily', 'Verdana, Geneva, sans-serif', 1),
(243, 'client', 'ui/players/nameText/fontSize', '12px', 1),
(244, 'client', 'ui/players/nameText/height', '-90', 2),
(245, 'client', 'ui/players/nameText/shadowBlur', '5', 2),
(246, 'client', 'ui/players/nameText/shadowColor', 'rgba(0,0,0,0.7)', 1),
(247, 'client', 'ui/players/nameText/shadowX', '5', 2),
(248, 'client', 'ui/players/nameText/shadowY', '5', 2),
(249, 'client', 'ui/players/nameText/stroke', '#000000', 1),
(250, 'client', 'ui/players/nameText/strokeThickness', '4', 2),
(251, 'client', 'ui/players/nameText/textLength', '4', 2),
(252, 'client', 'ui/players/showNames', '1', 3),
```
New configuration to add:
```sql
(253, 'client', 'ui/players/showCurrentPlayerName', '0', 3),
```
## Configuration Examples
### Example 1: Hide Current Player Name
```sql
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/players/showCurrentPlayerName';
```
Current player's name will not be displayed. Useful when using alternative UI systems.
### Example 2: Hide All Other Players' Names
```sql
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/players/showNames';
```
Other players' names will not be displayed. Current player's name visibility depends on `showCurrentPlayerName`.
### Example 3: Show Both Current Player and Other Players' Names
```sql
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/players/showCurrentPlayerName';
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/players/showNames';
```
All players' names will be displayed.
### Example 4: Customize Name Text Style
```sql
UPDATE `config` SET `value` = '#00ff00' WHERE `scope` = 'client' AND `path` = 'ui/players/nameText/fill';
UPDATE `config` SET `value` = '16px' WHERE `scope` = 'client' AND `path` = 'ui/players/nameText/fontSize';
UPDATE `config` SET `value` = '6' WHERE `scope` = 'client' AND `path` = 'ui/players/nameText/strokeThickness';
```
Creates green player names with 16px font size and thicker stroke.
## Visibility Behavior
**Configuration: showCurrentPlayerName=0, showNames=0**
- Current Player: Name hidden
- Other Players: Names hidden
**Configuration: showCurrentPlayerName=0, showNames=1**
- Current Player: Name hidden
- Other Players: Names shown
**Configuration: showCurrentPlayerName=1, showNames=0**
- Current Player: Name shown
- Other Players: Names hidden
**Configuration: showCurrentPlayerName=1, showNames=1**
- Current Player: Name shown
- Other Players: Names shown
## Implementation Details
### Files
- **PlayerEngine**: `lib/users/client/player-engine.js` - Main player management class
- **SpriteTextFactory**: `lib/game/client/engine/sprite-text-factory.js` - Text rendering utility
### Key Methods
- `showPlayerName(id)`: Displays name above player sprite, checks configuration
- `updateNamePosition(playerSprite)`: Updates name position during movement
- `applyNameLengthLimit(showName)`: Truncates long names
### Events
- `reldens.playerEngineAddPlayer`: Called when player is added, triggers name display
- `reldens.runPlayerAnimation`: Updates name position during animation
---
# Life Bar Configuration
## Overview
The life bar system displays health bars for the current player, other players, NPCs, and enemies. Life bars are rendered using Phaser graphics and can be positioned either fixed on the UI scene or floating above sprites.
## Configuration Path
**Scope**: `client`
**Path**: `ui/lifeBar`
**Type**: Multiple (boolean, number, string)
## Configuration Properties
### Core Settings
- **enabled** (type 3 - boolean): Enable or disable the entire lifebar system
- Default: `1` (enabled)
- Database: `client/ui/lifeBar/enabled`
### Visual Appearance
- **fillStyle** (type 1 - string): Hex color for the filled portion of the bar
- Default: `0xff0000` (red)
- Database: `client/ui/lifeBar/fillStyle`
- Format: Hex color without `#` prefix (e.g., `0xff0000`)
- **lineStyle** (type 1 - string): Hex color for the bar border
- Default: `0xffffff` (white)
- Database: `client/ui/lifeBar/lineStyle`
- Format: Hex color without `#` prefix (e.g., `0xffffff`)
- **height** (type 2 - number): Height of the bar in pixels
- Default: `5`
- Database: `client/ui/lifeBar/height`
- **width** (type 2 - number): Width of the bar in pixels
- Default: `50`
- Database: `client/ui/lifeBar/width`
- **top** (type 2 - number): Distance above sprite in pixels
- Default: `5`
- Database: `client/ui/lifeBar/top`
### Positioning
The lifebar system supports two positioning modes: fixed and floating.
#### Fixed Position
- **fixedPosition** (type 3 - boolean): Current player's bar appears at fixed position on UI scene
- Default: `0` (floating above sprite)
- Database: `client/ui/lifeBar/fixedPosition`
- When enabled, uses `x`, `y`, `responsiveX`, `responsiveY` properties
- **x** (type 2 - number): Fixed X position in pixels
- Default: `5`
- Database: `client/ui/lifeBar/x`
- Used when `fixedPosition: 1` and responsive mode is disabled
- **y** (type 2 - number): Fixed Y position in pixels
- Default: `12`
- Database: `client/ui/lifeBar/y`
- Used when `fixedPosition: 1` and responsive mode is disabled
#### Responsive Positioning
- **responsiveX** (type 2 - number): Responsive X position as percentage of screen width
- Default: `1`
- Database: `client/ui/lifeBar/responsiveX`
- Calculation: `uiX = responsiveX * screenWidth / 100`
- Used when `fixedPosition: 1` and `client/ui/screen/responsive` is enabled
- **responsiveY** (type 2 - number): Responsive Y position as percentage of screen height
- Default: `24`
- Database: `client/ui/lifeBar/responsiveY`
- Calculation: `uiY = responsiveY * screenHeight / 100`
- Used when `fixedPosition: 1` and `client/ui/screen/responsive` is enabled
### Visibility Controls
- **showCurrentPlayer** (type 3 - boolean): Show lifebar for the current player
- Default: `0` (hidden)
- Database: `client/ui/lifeBar/showCurrentPlayer`
- When disabled, current player's lifebar will not be displayed
- Useful when using alternative UI systems like player stats bars
- **showAllPlayers** (type 3 - boolean): Show lifebars for all other players
- Default: `0` (hidden)
- Database: `client/ui/lifeBar/showAllPlayers`
- When disabled, other players' bars only show via `showOnClick`
- **showEnemies** (type 3 - boolean): Show lifebars for NPCs and enemies
- Default: `1` (enabled)
- Database: `client/ui/lifeBar/showEnemies`
- Controls all objects (NPCs/enemies)
- **showOnClick** (type 3 - boolean): Show lifebars only when target is clicked
- Default: `1` (enabled)
- Database: `client/ui/lifeBar/showOnClick`
- Works for both other players and objects when their specific show flags are disabled
## Database Configuration
### Development Migration
Add to `migrations/development/[version]-sql-update.sql`:
```sql
INSERT INTO `config` (`scope`, `path`, `value`, `type`) VALUES
('client', 'ui/lifeBar/enabled', '1', 3),
('client', 'ui/lifeBar/fillStyle', '0xff0000', 1),
('client', 'ui/lifeBar/fixedPosition', '0', 3),
('client', 'ui/lifeBar/height', '5', 2),
('client', 'ui/lifeBar/lineStyle', '0xffffff', 1),
('client', 'ui/lifeBar/responsiveX', '1', 2),
('client', 'ui/lifeBar/responsiveY', '24', 2),
('client', 'ui/lifeBar/showAllPlayers', '0', 3),
('client', 'ui/lifeBar/showCurrentPlayer', '0', 3),
('client', 'ui/lifeBar/showEnemies', '1', 3),
('client', 'ui/lifeBar/showOnClick', '1', 3),
('client', 'ui/lifeBar/top', '5', 2),
('client', 'ui/lifeBar/width', '50', 2),
('client', 'ui/lifeBar/x', '5', 2),
('client', 'ui/lifeBar/y', '12', 2);
```
### Production Migration
From `migrations/production/reldens-basic-config-v4.0.0.sql` (IDs 181-194):
```sql
(181, 'client', 'ui/lifeBar/enabled', '1', 3),
(182, 'client', 'ui/lifeBar/fillStyle', '0xff0000', 1),
(183, 'client', 'ui/lifeBar/fixedPosition', '0', 3),
(184, 'client', 'ui/lifeBar/height', '5', 2),
(185, 'client', 'ui/lifeBar/lineStyle', '0xffffff', 1),
(186, 'client', 'ui/lifeBar/responsiveX', '1', 2),
(187, 'client', 'ui/lifeBar/responsiveY', '24', 2),
(188, 'client', 'ui/lifeBar/showAllPlayers', '0', 3),
(189, 'client', 'ui/lifeBar/showCurrentPlayer', '0', 3),
(190, 'client', 'ui/lifeBar/showEnemies', '1', 3),
(191, 'client', 'ui/lifeBar/showOnClick', '1', 3),
(192, 'client', 'ui/lifeBar/top', '5', 2),
(193, 'client', 'ui/lifeBar/width', '50', 2),
(194, 'client', 'ui/lifeBar/x', '5', 2),
(195, 'client', 'ui/lifeBar/y', '12', 2),
```
## Configuration Examples
### Example 1: Fixed Position in Top-Left Corner
```sql
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/fixedPosition';
UPDATE `config` SET `value` = '5' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/x';
UPDATE `config` SET `value` = '5' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/y';
```
This positions the current player's lifebar at coordinates (5, 5) on the UI scene, fixed regardless of player movement.
### Example 2: Responsive Fixed Position
```sql
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/fixedPosition';
UPDATE `config` SET `value` = '50' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/responsiveX';
UPDATE `config` SET `value` = '5' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/responsiveY';
```
This positions the current player's lifebar at 50% of screen width and 5% of screen height, adapting to different resolutions.
### Example 3: Show All Players' Lifebars
```sql
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showAllPlayers';
```
All other players' lifebars are always visible, floating above their sprites.
### Example 4: Hide Enemy Lifebars
```sql
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showEnemies';
```
NPCs and enemies will not show lifebars at all.
### Example 5: Hide Current Player Lifebar
```sql
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showCurrentPlayer';
```
Current player's lifebar will not be displayed. Useful when using alternative UI systems like player stats bars.
### Example 6: Always Show Bars (No Click Required)
```sql
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showOnClick';
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showAllPlayers';
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showEnemies';
```
All players and enemies will always show their lifebars without requiring click interaction.
### Example 7: Custom Colors and Dimensions
```sql
UPDATE `config` SET `value` = '0x00ff00' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/fillStyle';
UPDATE `config` SET `value` = '0x000000' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/lineStyle';
UPDATE `config` SET `value` = '80' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/width';
UPDATE `config` SET `value` = '8' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/height';
```
Creates a green lifebar with black border, 80 pixels wide and 8 pixels tall.
## Visibility Behavior
The current player's lifebar visibility is controlled by `showCurrentPlayer` configuration.
**Configuration: showCurrentPlayer=0, showAllPlayers=0, showEnemies=0, showOnClick=0**
- Current Player: Never
- Other Players: Never
- NPCs/Enemies: Never
**Configuration: showCurrentPlayer=0, showAllPlayers=0, showEnemies=0, showOnClick=1**
- Current Player: Never
- Other Players: On Click
- NPCs/Enemies: Never
**Configuration: showCurrentPlayer=0, showAllPlayers=0, showEnemies=1, showOnClick=1**
- Current Player: Never
- Other Players: On Click
- NPCs/Enemies: On Click
**Configuration: showCurrentPlayer=1, showAllPlayers=0, showEnemies=0, showOnClick=0**
- Current Player: Always
- Other Players: Never
- NPCs/Enemies: Never
**Configuration: showCurrentPlayer=1, showAllPlayers=0, showEnemies=1, showOnClick=1**
- Current Player: Always
- Other Players: On Click
- NPCs/Enemies: On Click
**Configuration: showCurrentPlayer=1, showAllPlayers=1, showEnemies=0, showOnClick=0**
- Current Player: Always
- Other Players: Always
- NPCs/Enemies: Never
**Configuration: showCurrentPlayer=1, showAllPlayers=1, showEnemies=1, showOnClick=0**
- Current Player: Always
- Other Players: Always
- NPCs/Enemies: Always
## Positioning Behavior
### Floating Mode (fixedPosition: 0)
- Current player's bar floats above sprite
- Other players' bars float above their sprites
- NPCs/enemies bars float above their sprites
- Bars automatically update position as sprites move
- Position calculation: `(spriteX - barWidth/2, spriteY - barHeight - top + spriteTopOffset/2)`
### Fixed Mode (fixedPosition: 1)
- **Current player only**: Bar appears at fixed position on UI scene
- Other players and NPCs/enemies always float above sprites
- Fixed position uses either:
- Absolute coordinates: `x`, `y` properties (when responsive is disabled)
- Responsive coordinates: `responsiveX`, `responsiveY` properties (when `client/ui/screen/responsive` is enabled)
- Bar position updates on screen resize
## Implementation Details
### Files
- **LifebarUi**: `lib/users/client/lifebar-ui.js` - Main lifebar management class
- **ObjectsHandler**: `lib/users/client/objects-handler.js` - Handles NPCs/enemies lifebars
- **Plugin**: `lib/users/client/plugin.js` - Initializes lifebar system
### Events
- `reldens.playerStatsUpdateAfter`: Updates current player's lifebar
- `reldens.joinedRoom`: Sets up message listeners for lifebar updates
- `reldens.runPlayerAnimation`: Redraws player lifebar
- `reldens.updateGameSizeBefore`: Recalculates fixed position on resize
- `reldens.playersOnRemove`: Removes player lifebar on disconnect
- `reldens.playerEngineAddPlayer`: Processes queued lifebar messages
- `reldens.createAnimationAfter`: Draws object lifebars
- `reldens.objectBodyChanged`: Updates object lifebar
- `reldens.gameEngineShowTarget`: Shows target lifebar on click
- `reldens.gameEngineClearTarget`: Hides previous target lifebar
### Bar Property
The lifebar tracks the stat configured at `client/actions/skills/affectedProperty`, which defaults to `hp`.
To change the tracked stat:
```sql
UPDATE `config` SET `value` = 'mp' WHERE `scope` = 'client' AND `path` = 'actions/skills/affectedProperty';
```
This would make lifebars track magic points instead of health points.
================================================
FILE: .claude/storage-architecture.md
================================================
# Storage & Entity Management Architecture
Complete reference for the storage system and entity management.
## Entity Generation Workflow
1. Define database schema (SQL migrations in `migrations/`)
2. Run `reldens generateEntities --override`
3. Entities are generated in `generated-entities/`
4. Models in each feature's `server/models/` extend generated entities
## Storage Drivers
- `objection-js` (default was objection-js): Uses Knex.js for SQL, direct database access, no validation
- `mikro-orm`: ORM with decorators, supports MongoDB
- `prisma` (current default): Modern ORM with type safety, custom validation, database default support
- Configured via `RELDENS_STORAGE_DRIVER` in `.env`
## Driver Differences
### ObjectionJS
- Direct SQL via Knex query builder
- No field validation before database
- Database handles defaults and constraints
- Foreign keys as direct field values
- Less informative error messages
### Prisma
- Type-safe Prisma Client
- Custom `ensureRequiredFields()` validation before database
- Skips validation for fields with database defaults
- Foreign keys use relation connect syntax: `{players: {connect: {id: 1001}}}`
- VARCHAR foreign key support
- Better error messages for missing required fields
- Metadata-driven field type casting
## Entity Access and Storage System Architecture
### CRITICAL: Understanding getEntity() Return Type
`dataServer.getEntity()` returns a `BaseDriver` instance from `@reldens/storage`, NOT an Entity or Model class.
**What getEntity() Returns:**
```javascript
// Returns BaseDriver instance (or ObjectionJsDriver, PrismaDriver, MikroOrmDriver subclass)
let statsRepository = this.dataServer.getEntity('stats');
// BaseDriver provides unified interface across all storage drivers:
await statsRepository.create({key: 'hp', label: 'Health Points'});
await statsRepository.loadAll();
await statsRepository.loadBy('key', 'hp');
await statsRepository.loadOneBy('key', 'hp');
await statsRepository.updateById(1, {label: 'HP'});
await statsRepository.deleteById(1);
```
**Type Annotation for Repository Properties:**
```javascript
/**
* @typedef {import('@reldens/storage').BaseDriver} BaseDriver
*/
// Correct - driver-agnostic type
/** @type {BaseDriver} */
this.statsRepository = this.dataServer.getEntity('stats');
// WRONG - Entity classes are for admin panel config only
/** @type {StatsEntity} */ // ❌ WRONG
this.statsRepository = this.dataServer.getEntity('stats');
// WRONG - Model classes are driver-specific (objection-js/prisma/mikro-orm)
/** @type {StatsModel} */ // ❌ WRONG
this.statsRepository = this.dataServer.getEntity('stats');
```
## Storage System Component Breakdown
### 1. Entity Classes (`generated-entities/entities/[table]-entity.js`)
- Purpose: Admin panel configuration ONLY
- Define property metadata (types, required fields, display names)
- Define edit/show/list properties for admin UI
- Example: `StatsEntity.propertiesConfig()` returns admin panel config
- Never used for database operations
### 2. Model Classes (`generated-entities/models/{driver}/[table]-model.js`)
- Purpose: ORM-specific model definitions
- Driver-specific paths:
- `models/objection-js/stats-model.js` - ObjectionJS
- `models/prisma/stats-model.js` - Prisma
- `models/mikro-orm/stats-model.js` - MikroORM
- Define table names, relations, schema
- Wrapped by BaseDriver before use
### 3. BaseDriver (`@reldens/storage/lib/base-driver.js`)
- Purpose: Unified database interface
- Wraps raw Model classes
- Provides consistent API across all storage drivers
- THIS IS WHAT `getEntity()` RETURNS
- Methods: create, load, loadBy, loadOneBy, update, delete, count, etc.
- Driver implementations:
- `ObjectionJsDriver` - uses Knex query builder
- `PrismaDriver` - uses Prisma Client
- `MikroOrmDriver` - uses MikroORM EntityManager
### 4. BaseDataServer (`@reldens/storage/lib/base-data-server.js`)
- Purpose: Manages database connection and entity registry
- Has `EntityManager` for storing BaseDriver instances
- `getEntity(key)` retrieves BaseDriver from EntityManager
- Driver implementations:
- `ObjectionJsDataServer`
- `PrismaDataServer`
- `MikroOrmDataServer`
## Entity Loading Flow
1. `EntitiesLoader.loadEntities()` (lib/game/server/entities-loader.js:41)
- Checks `RELDENS_STORAGE_DRIVER` env var (default: 'prisma')
- Loads from `generated-entities/models/{driver}/registered-models-{driver}.js`
- Returns `{entities, entitiesRaw, translations}`
2. `DataServerInitializer.initializeEntitiesAndDriver()` (lib/game/server/data-server-initializer.js:55)
- Creates DataServer instance: `new DriversMap[storageDriver](config)`
- DataServer generates BaseDriver instances for each entity
- Stores in EntityManager registry
3. `dataServer.getEntity(key)` returns BaseDriver from EntityManager
## Usage Examples
```javascript
// 1. Basic CRUD operations
let statsRepo = this.dataServer.getEntity('stats');
let newStat = await statsRepo.create({key: 'hp', label: 'Health'});
let allStats = await statsRepo.loadAll();
let hpStat = await statsRepo.loadOneBy('key', 'hp');
await statsRepo.updateById(hpStat.id, {base_value: 100});
// 2. With relations
let skillData = await this.dataServer
.getEntity('skillsClassLevelUpAnimations')
.loadAllWithRelations();
// 3. Accessing related data from loaded instances
let classPathModel = await this.dataServer.getEntity('skillsClassPath').loadById(1);
let relatedSkills = classPathModel.related_skills_levels_set.related_skills_levels;
```
## Important Notes
- ALWAYS use `BaseDriver` type for repository properties
- Entity classes are NEVER used for database operations
- Model classes are wrapped by BaseDriver - never accessed directly
- Storage driver is configurable: objection-js, prisma (default), mikro-orm
- Relations can be nested
- Entity relations keys are defined in `generated-entities/entities-config.js`
- Custom entity overrides are in `lib/[plugin-folder]/server/entities` or `lib/[plugin-folder]/server/models`
## Generated Entities Structure
The `generated-entities/` directory contains:
- `entities/` - 60+ auto-generated entity classes for all database tables
- `models/` - Custom entity overrides (extend generated entities)
- `entities-config.js` - Entity relationship mappings and configuration
- `entities-translations.js` - Translation/label mappings for admin panel
## Entity Overrides and Database Defaults
**Auto-Populated Fields:**
Some fields should be auto-populated by the database or application logic, not manually entered through the admin panel.
**Example: scores_detail.kill_time**
```javascript
// Database schema (migrations/production/reldens-install-v4.0.0.sql)
// `kill_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
// Entity override (lib/scores/server/entities/scores-detail-entity-override.js)
class ScoresDetailEntityOverride extends ScoresDetailEntity {
static propertiesConfig(extraProps) {
let config = super.propertiesConfig(extraProps);
// Remove kill_time from admin panel edit form
config.editProperties.splice(config.editProperties.indexOf('kill_time'), 1);
return config;
}
}
// Game logic auto-populates when creating through code
// (lib/scores/server/scores-updater.js)
let scoreDetailData = {
player_id: attacker.player_id,
obtained_score: obtainedScore,
kill_time: sc.formatDate(new Date()), // Auto-populated
kill_player_id: props.killPlayerId || null,
kill_npc_id: props.killNpcId || null,
};
```
**How It Works:**
1. Field removed from `editProperties` - not shown in admin panel
2. Database has `DEFAULT CURRENT_TIMESTAMP` - auto-fills when missing
3. Game logic explicitly sets value when creating programmatically
4. Prisma driver skips validation for fields with database defaults
**Important:** With Prisma driver, validation automatically skips required fields that have database defaults, allowing admin panel creates to succeed even when these fields are excluded from the form.
================================================
FILE: .claude/trade-system-implementation.md
================================================
# Trade System Flow - Player-to-Player Trading
## Server to Client Data Flow
**Server sends to each player (via TRADE_SHOW message):**
- `playerToExchangeKey`: The OTHER player's exchange key ('A' or 'B')
- `playerConfirmed`: The OTHER player's confirmation status (for display message)
- `myConfirmed`: THIS player's confirmation status (for button state logic)
- `items`: THIS player's available inventory items (for column 1)
- `traderItemsData`: The OTHER player's item data (for column 3 display)
- `exchangeData`: Complete exchange object with structure: `{ 'A': {itemUid: qty}, 'B': {itemUid: qty} }`
- `isTradeEnd`: Boolean indicating if both players confirmed (triggers trade completion)
**Server determines playerToExchangeKey:**
Line 305 in `lib/inventory/server/message-actions.js`:
```javascript
let playerToExchangeKey = ownerSessionId === playerTo.sessionId ? 'A' : 'B';
```
This identifies which exchange key belongs to the OTHER player (the one being sent data about in the message).
## Three Column Structure
The trade UI displays three columns:
- **Column 1** (`.my-items`): My available inventory items - items I can add to trade
- **Column 2** (`.pushed-to-trade`): Items I'M SENDING to the other player
- **Column 3** (`.got-from-trade`): Items I'M RECEIVING from the other player
**HTML Structure:**
- `.trade-container`
- `.trade-row.trade-items-boxes`
- `.trade-player-col.trade-col-1.my-items` (My Items)
- `.trade-player-col.trade-col-2.pushed-to-trade` (Sending)
- `.trade-player-col.trade-col-3.got-from-trade` (Receiving)
- `.trade-row.trade-confirm-actions`
- `.confirm-action` button
- `.disconfirm-action` button
- `.cancel-action` button
## Client Processing Flow
**When client receives TRADE_SHOW message:**
Line 136-141 in `trade-message-handler.js`:
```javascript
let traderExchangeKey = sc.get(this.message, 'playerToExchangeKey', 'A');
let myExchangeKey = 'A' === traderExchangeKey ? 'B' : 'A';
this.updateItemsList(items, container, exchangeData[myExchangeKey]);
this.updateMyExchangeData((exchangeData[myExchangeKey] || {}), items, myExchangeKey);
this.updateTraderExchangeData((exchangeData[traderExchangeKey] || {}), traderItemsData, traderExchangeKey);
```
**Processing steps:**
1. Extract exchange keys:
- `traderExchangeKey` = value from `playerToExchangeKey` (OTHER player's key)
- `myExchangeKey` = opposite of `traderExchangeKey` (THIS player's key)
2. Update Column 1 (my available items):
- Call `updateItemsList(items, container, exchangeData[myExchangeKey])`
- Passes MY exchange data to check which items to hide (items with full qty in trade)
3. Update Column 2 (items I'm sending):
- Call `updateMyExchangeData(exchangeData[myExchangeKey], items, myExchangeKey)`
- Shows items from MY exchange key
4. Update Column 3 (items I'm receiving):
- Call `updateTraderExchangeData(exchangeData[traderExchangeKey], traderItemsData, traderExchangeKey)`
- Shows items from TRADER's exchange key
## HTML Recreation Pattern
**Every TRADE_SHOW message triggers full HTML recreation:**
Line 181 in `trade-message-handler.js`:
```javascript
container.innerHTML = this.createTradeContainer(tradeItems);
```
Server sends TRADE_SHOW to BOTH players simultaneously when:
- Item added/removed
- Player confirms/disconfirms
- ANY trade state change
**Implications:**
- All buttons and DOM elements are DESTROYED and RECREATED each time
- Event listeners must be re-attached after every update (lines 182-183)
- Server state is the ONLY source of truth
- No client-side state should be maintained between updates
## Button State Logic
**Server sends confirmation statuses:**
- `playerConfirmed`: OTHER player's confirmation status (for display message "Player X CONFIRMED")
- `myConfirmed`: THIS player's confirmation status (for button state logic)
**Button States Calculation:**
Line 183 in `trade-message-handler.js`:
```javascript
this.activateConfirmButtonAction(sc.get(this.message, 'exchangeData', {}));
```
Lines 193-200 in `activateConfirmButtonAction`:
```javascript
let myExchangeKey = sc.get(this.message, 'playerToExchangeKey', 'A');
let traderExchangeKey = 'A' === myExchangeKey ? 'B' : 'A';
let myExchangeData = exchangeData[myExchangeKey] || {};
let traderExchangeData = exchangeData[traderExchangeKey] || {};
let myHasItems = 0 < Object.keys(myExchangeData).length;
let traderHasItems = 0 < Object.keys(traderExchangeData).length;
let hasAnyItems = myHasItems || traderHasItems;
let iConfirmed = sc.get(this.message, 'myConfirmed', false);
```
**Confirm Button:**
- `disabled = iConfirmed || !hasAnyItems`
- Disabled when: Player already confirmed OR no items in trade
- Enabled when: Player not confirmed AND items exist in trade
**Disconfirm Button:**
- `disabled = !iConfirmed`
- Disabled when: Player not confirmed
- Enabled when: Player already confirmed
**Each player sees their own button states based on their own confirmation status from `myConfirmed` field.**
## Example Data Flow
**Scenario: Player A (key='A') adds itemX to trade, then confirms**
**Server state after adding item:**
```javascript
exchangeData = {
'A': {itemX: 1},
'B': {}
}
confirmations = {
'A': false,
'B': false
}
```
**Message sent to Player A:**
```javascript
{
playerToExchangeKey: 'B',
playerConfirmed: false,
myConfirmed: false,
exchangeData: { 'A': {itemX: 1}, 'B': {} },
items: {...},
traderItemsData: {}
}
```
**Player A UI state:**
- Column 1: Shows Player A's available items (itemX hidden if full qty placed)
- Column 2: Shows `exchangeData['A']` = {itemX: 1} (sending to Player B)
- Column 3: Shows `exchangeData['B']` = {} (receiving from Player B - empty)
- Confirm button: ENABLED (myConfirmed=false, hasAnyItems=true)
- Disconfirm button: DISABLED (myConfirmed=false)
**Message sent to Player B:**
```javascript
{
playerToExchangeKey: 'A',
playerConfirmed: false,
myConfirmed: false,
exchangeData: { 'A': {itemX: 1}, 'B': {} },
items: {...},
traderItemsData: {itemX: {...}}
}
```
**Player B UI state:**
- Column 1: Shows Player B's available items
- Column 2: Shows `exchangeData['B']` = {} (sending to Player A - empty)
- Column 3: Shows `exchangeData['A']` = {itemX: 1} (receiving from Player A)
- Display message: No confirmation message (playerConfirmed=false)
- Confirm button: ENABLED (myConfirmed=false, hasAnyItems=true)
- Disconfirm button: DISABLED (myConfirmed=false)
**After Player A clicks confirm:**
Server updates confirmations:
```javascript
confirmations = {
'A': true,
'B': false
}
```
**Message sent to Player A:**
```javascript
{
playerToExchangeKey: 'B',
playerConfirmed: false,
myConfirmed: true,
// ... rest same
}
```
**Player A UI state:**
- Confirm button: DISABLED (myConfirmed=true)
- Disconfirm button: ENABLED (myConfirmed=true)
**Message sent to Player B:**
```javascript
{
playerToExchangeKey: 'A',
playerConfirmed: true,
myConfirmed: false,
// ... rest same
}
```
**Player B UI state:**
- Display message: "Player A CONFIRMED" (playerConfirmed=true)
- Confirm button: ENABLED (myConfirmed=false)
- Disconfirm button: DISABLED (myConfirmed=false)
## Toggle Actions (Column 1 Only)
**CSS Behavior (lines 373-405 in items-system.scss):**
```scss
.my-items .trade-item {
.actions-container.trade-actions {
display: none; // Hidden by default
&.trade-actions-expanded {
display: block; // Visible when toggled
position: absolute; // Float below item
top: 54px;
left: 0;
z-index: 3;
background: $cBlack;
border: 1px solid $cWhite;
border-radius: 6px;
padding: 4px;
}
}
}
```
**Important:** Toggle behavior with absolute positioning applies ONLY to column 1 (`.my-items`). Columns 2 and 3 do not have toggle behavior - their actions are always visible.
## Files Involved
**Client:**
- `lib/inventory/client/trade-message-handler.js` - Main trade UI handler
- `lib/inventory/client/trade-items-helper.js` - Item instance creation
- `theme/default/css/items-system.scss` - Trade UI styles
**Server:**
- `lib/inventory/server/message-actions.js` - Trade message handling
- `lib/inventory/server/trade.js` - Trade logic
**Constants:**
- `lib/objects/constants.js` - Trade action constants (ADD, REMOVE, CONFIRM, DISCONFIRM)
- `lib/inventory/constants.js` - Inventory action constants (TRADE_START, TRADE_SHOW, etc.)
**Translations:**
- `lib/inventory/client/snippets/en_US.js` - UI labels (trade.actions.disconfirm)
## CSS Styling
**Player Confirmed Message** (lines 268-284 in items-system.scss):
- Styled block with border and background
- Empty state handling with transparent background
**Button Layout** (lines 303-310):
- Flexbox with center justification
- No float positioning
**Remove Button** (lines 358-366):
- Absolute positioning at `right: -10px`
- Icon size 20px
**Toggle Actions** (lines 373-405):
- Scoped to `.my-items` column only
- Absolute positioning with floating styles
- Other columns display actions inline without toggle
================================================
FILE: .claude/ui-visibility-configuration.md
================================================
# UI Visibility Configuration
## Overview
This document describes the configuration system for controlling visibility of UI elements that can be displayed separately for the current player versus other players and NPCs.
---
## Life Bar Visibility Configuration
### Purpose
Controls the display of health bars above player and NPC sprites. Allows independent configuration for current player, other players, and NPCs/enemies.
### Configuration Paths
- **Scope**: `client`
- **Base Path**: `ui/lifeBar`
- **Type**: boolean (type 3)
### Visibility Properties
**showCurrentPlayer**
- Path: `client/ui/lifeBar/showCurrentPlayer`
- Default: `0` (disabled)
- Controls: Current player's lifebar visibility
- Use case: Disable when using alternative UI systems like stat bars in player info panel
**showAllPlayers**
- Path: `client/ui/lifeBar/showAllPlayers`
- Default: `0` (disabled)
- Controls: Other players' lifebars visibility
- Use case: Enable for PvP-focused games where seeing other players' health is important
**showEnemies**
- Path: `client/ui/lifeBar/showEnemies`
- Default: `1` (enabled)
- Controls: NPCs and enemies lifebars visibility
- Use case: Disable for less cluttered visual experience
**showOnClick**
- Path: `client/ui/lifeBar/showOnClick`
- Default: `1` (enabled)
- Controls: Whether lifebars show only when target is clicked
- Works for: Both other players and objects when their specific show flags are disabled
### Implementation Flow
**File**: `lib/users/client/lifebar-ui.js`
**Method**: `canShowPlayerLifeBar(playerId)`
Flow:
1. Check if player is current player by comparing playerId with gameManager.getCurrentPlayer().playerId
2. If current player: return value of `barConfig.showCurrentPlayer`
3. If other player: check `barConfig.showAllPlayers` first, then `barConfig.showOnClick` if false
4. Draw lifebar only if check returns true
**Customizable Fields**:
- `showCurrentPlayer` - boolean - stored in `this.barConfig.showCurrentPlayer`
- `showAllPlayers` - boolean - stored in `this.barConfig.showAllPlayers`
- `showEnemies` - boolean - stored in `this.barConfig.showEnemies`
- `showOnClick` - boolean - stored in `this.barConfig.showOnClick`
### Configuration Examples
Hide current player lifebar:
```sql
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showCurrentPlayer';
```
Show all players lifebars always:
```sql
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showAllPlayers';
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showOnClick';
```
Hide all lifebars:
```sql
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showCurrentPlayer';
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showAllPlayers';
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showEnemies';
```
---
## Player Names Visibility Configuration
### Purpose
Controls the display of character names above player sprites. Allows independent configuration for current player versus other players.
### Configuration Paths
- **Scope**: `client`
- **Base Path**: `ui/players`
- **Type**: boolean (type 3)
### Visibility Properties
**showCurrentPlayerName**
- Path: `client/ui/players/showCurrentPlayerName`
- Default: `0` (disabled)
- Controls: Current player's name visibility
- Use case: Disable for cleaner visual experience when player info is shown in UI panel
**showNames**
- Path: `client/ui/players/showNames`
- Default: `1` (enabled)
- Controls: Other players' names visibility
- Use case: Disable for less cluttered multiplayer experience
**showNamesLimit**
- Path: `client/ui/players/showNamesLimit`
- Default: `10`
- Controls: Maximum name length before truncation with ellipsis
- Use case: Prevent long names from cluttering the screen
### Implementation Flow
**File**: `lib/users/client/player-engine.js`
**Method**: `showPlayerName(id)`
Flow:
1. Determine which config to check using ternary: `id === this.playerId ? showCurrentPlayerName : showNames`
2. Return false if config value is false
3. Validate player exists and has name property
4. Apply name length limit if configured
5. Attach text sprite to player using SpriteTextFactory
**Method**: `updateNamePosition(playerSprite)`
Flow:
1. Determine which config to check: `playerId === this.playerId ? showCurrentPlayerName : showNames`
2. Return false if config is disabled or nameSprite doesn't exist
3. Calculate relative position and update sprite coordinates
**Customizable Fields**:
- `globalConfigShowCurrentPlayerName` - boolean - loaded from `client/ui/players/showCurrentPlayerName`
- `globalConfigShowNames` - boolean - loaded from `client/ui/players/showNames`
- `globalConfigShowNamesLimit` - number - loaded from `client/ui/players/showNamesLimit`
- `globalConfigNameText` - object - loaded from `client/ui/players/nameText` with style properties
### Configuration Examples
Hide current player name:
```sql
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/players/showCurrentPlayerName';
```
Hide all other players names:
```sql
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/players/showNames';
```
Show both current and other players names:
```sql
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/players/showCurrentPlayerName';
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/players/showNames';
```
Increase name length limit:
```sql
UPDATE `config` SET `value` = '20' WHERE `scope` = 'client' AND `path` = 'ui/players/showNamesLimit';
```
---
## Common Patterns
### Pattern 1: Clean Current Player Display
When using custom UI panels for current player information:
```sql
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showCurrentPlayer';
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/players/showCurrentPlayerName';
```
Result: Current player has no floating UI elements, all info shown in panels
### Pattern 2: Minimal Multiplayer Display
For focused gameplay with minimal distractions:
```sql
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showAllPlayers';
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/players/showNames';
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showOnClick';
```
Result: Other players show info only when clicked
### Pattern 3: Full Visibility
For PvP or cooperative multiplayer:
```sql
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showCurrentPlayer';
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showAllPlayers';
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/players/showCurrentPlayerName';
UPDATE `config` SET `value` = '1' WHERE `scope` = 'client' AND `path` = 'ui/players/showNames';
UPDATE `config` SET `value` = '0' WHERE `scope` = 'client' AND `path` = 'ui/lifeBar/showOnClick';
```
Result: All players always show names and health bars
---
## Implementation Details
### Code Organization
Both systems follow the same architectural pattern:
1. Configuration loaded in constructor from gameManager.config
2. Single method determines visibility based on player type (current vs other)
3. Ternary operator selects appropriate config property
4. Early return if visibility check fails
5. Render or update UI element if check passes
### Property Access Pattern
Properties are stored as class instance variables for performance:
```javascript
this.barConfig = gameManager.config.get('client/ui/lifeBar');
this.globalConfigShowCurrentPlayerName = Boolean(this.config.get('client/ui/players/showCurrentPlayerName'));
this.globalConfigShowNames = Boolean(this.config.get('client/ui/players/showNames'));
```
### Conditional Logic Pattern
Both implementations use clean ternary logic:
```javascript
let shouldShow = id === this.playerId ? this.configForCurrent : this.configForOthers;
if(!shouldShow){
return false;
}
```
### Integration Points
**Life Bars**:
- Created in: `lib/users/client/plugin.js` during `reldens.beforeCreateEngine` event
- Updated on: `reldens.playerStatsUpdateAfter`, `reldens.runPlayerAnimation`, `reldens.updateGameSizeBefore`
- Removed on: `reldens.playersOnRemove`
**Player Names**:
- Created in: `lib/users/client/player-engine.js` during `addPlayer()` call
- Updated on: Every animation frame during `updatePlayerState()`
- Removed on: `removePlayer()` call
---
## Migration Notes
When adding these configurations to existing installations:
Development migration file:
```sql
INSERT INTO `config` (`scope`, `path`, `value`, `type`) VALUES
('client', 'ui/lifeBar/showCurrentPlayer', '0', 3),
('client', 'ui/players/showCurrentPlayerName', '0', 3);
```
Default values set to `0` to avoid changing existing behavior where alternative UI systems may already be implemented.
After migration, users can explicitly enable these features if desired.
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: reldens
open_collective: # Replace with a single Open Collective username
ko_fi: damianpastorini
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: damian-pastorini
issuehunt: damian-pastorini
otechie: # Replace with a single Otechie username
# Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
custom: https://www.paypal.com/paypalme/damianpastorini
================================================
FILE: .github/workflows/codeql.yml
================================================
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "48 17 * * 0"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ javascript ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"
================================================
FILE: .gitignore
================================================
/.cache
/.env
/.idea
/knexfile.js
/node_modules
/npm-debug*
/test
/ts-node*
/v8-compile-cache*
/dev
winpty.exe.stackdump
/.parcel-cache
/tests/config.json
/prisma
/.claude/settings.json
/.claude/settings.local.json
/.claude/instructions.md
/CLAUDE.local.md
/.claude/skills/
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Reldens is an MMORPG Platform (v4.0.0-beta.39) built on Node.js, designed for developers to create multiplayer games. The platform integrates:
- **Server**: Colyseus 0.16 for multiplayer game server
- **Client**: Phaser 3 for game engine, Parcel for bundling
- **Database**: Supports multiple storage drivers (objection-js, mikro-orm, prisma)
- **Architecture**: Client-server with authoritative server, real-time synchronization via WebSockets
**Node Version**: >= 20.0.0
### Sub-Packages
- **@reldens/utils** - Core utilities, Shortcuts class (imported as `sc`), EventsManagerSingleton, Logger
- **@reldens/server-utils** - Server utilities, FileHandler, configuration helpers
- **@reldens/storage** - Multi-ORM database layer (ObjectionJS, MikroORM, Prisma)
- **@reldens/cms** - Content management system and admin panel
- **@reldens/items-system** - Items and inventory system
- **@reldens/modifiers** - Stats and modifiers system
- **@reldens/skills** - Skills and abilities system
## Essential Commands
```bash
# Testing
npm test
node tests/manager.js --filter="test-name" --break-on-error
# Building
reldens buildSkeleton # Build both styles and client
reldens fullRebuild # Complete rebuild from scratch
# Database
reldens generateEntities [--override] # Generate entities from database schema
# User management
reldens createAdmin --user=username --pass=password --email=email@example.com
reldens resetPassword --user=username --pass=newpassword
```
**Full command reference**: See `.claude/commands-reference.md`
## Architecture Overview
### Client-Server Organization
The codebase follows a **client/server split architecture** within each feature module:
```
lib/
├── {feature}/
│ ├── client/ # Client-side code (Phaser, UI, rendering)
│ ├── server/ # Server-side code (Colyseus rooms, logic)
│ ├── constants.js # Shared constants
│ └── schemas/ # Colyseus state schemas (if applicable)
```
### Core Entry Points
- **Server**: `server.js` → `lib/game/server/manager.js` (ServerManager)
- **Client**: `client.js` → `lib/game/client/game-manager.js` (GameManager)
- **Theme**: `theme/default/index.js` initializes client with custom plugins
### Feature Modules (23 Total)
The platform includes 23 feature modules: Game, Rooms, World, Config, Features, Actions, Inventory, Respawn, Rewards, Scores, Teams, Users, Chat, Audio, Prediction, Admin, Firebase, Ads, Import, Objects, Snippets, Bundlers.
**Detailed list**: See `.claude/feature-modules.md`
## Configuration System
Reldens uses a **database-driven configuration** with runtime overrides:
1. **Database Config** (`config` table): Path-based keys, scoped by `scope` field
2. **Environment Variables** (`.env`): Prefix `RELDENS_*` for all settings
3. **Custom Classes**: Passed via `customClasses` to override defaults
**Key Environment Variables:**
- `RELDENS_STORAGE_DRIVER` - Storage driver (objection-js, mikro-orm, prisma)
- `RELDENS_DB_HOST`, `RELDENS_DB_PORT`, `RELDENS_DB_NAME`, `RELDENS_DB_USER`, `RELDENS_DB_PASSWORD`
- `RELDENS_HOT_PLUG` - Enable hot-plug configuration updates (0/1)
**Full environment variables list**: See `.claude/environment-variables.md`
## Events System
The platform uses **@reldens/utils EventsManagerSingleton** for extensibility:
**Common Event Patterns**:
- `reldens.{action}Before` - Hook before operation
- `reldens.{action}After` - Hook after operation
- Events are synchronous (`emitSync`) or async (`emit`)
**Key Events**:
- `reldens.serverConfigFeaturesReady` - Features loaded
- `reldens.beforeJoinGame` - Before player joins
- `reldens.startGameAfter` - Game initialized
- `reldens.roomLoginOnAuth` - Custom authentication logic
**Plugin Pattern**:
```javascript
class ServerPlugin {
setup({events}) {
events.on('reldens.serverConfigFeaturesReady', (props) => {
// Custom logic here
});
}
}
```
## Storage & Entity Management
### CRITICAL: Understanding getEntity()
`dataServer.getEntity()` returns a `BaseDriver` instance from `@reldens/storage`, NOT an Entity or Model class.
```javascript
// Correct - returns BaseDriver
let statsRepository = this.dataServer.getEntity('stats');
// BaseDriver provides unified interface:
await statsRepository.create({key: 'hp', label: 'Health Points'});
await statsRepository.loadAll();
await statsRepository.loadOneBy('key', 'hp');
await statsRepository.updateById(1, {label: 'HP'});
```
**Type Annotation:**
```javascript
/** @type {import('@reldens/storage').BaseDriver} */
this.statsRepository = this.dataServer.getEntity('stats');
```
**Storage Drivers:**
- `prisma` (current default): Modern ORM with type safety, custom validation
- `objection-js`: Uses Knex.js, direct SQL, no validation
- `mikro-orm`: ORM with decorators, supports MongoDB
**Detailed architecture**: See `.claude/storage-architecture.md`
**Entity list**: See `.claude/entities-reference.md`
## Theme & Customization
**Theme Structure** (`theme/`):
- `plugins/` - Custom client/server plugins for game-specific logic
- `default/` - Default theme assets (HTML, CSS, sprites, audio)
- `admin/` - Admin panel customizations
**Theme Management:**
ThemeManager (`lib/game/server/theme-manager.js`) handles asset copying, bundling, and CSS compilation.
### Client Bundling Best Practices
**CRITICAL**: Always use `themeManager.createClientBundle()` instead of calling `buildClient()` or `buildCss()` directly:
- **createClientBundle()** - Wrapper that checks `RELDENS_ALLOW_RUN_BUNDLER` environment variable (used during server startup)
- **buildClient()** - Direct method that checks `RELDENS_ALLOW_BUILD_CLIENT` environment variable
- **buildCss()** - Direct method that checks `RELDENS_ALLOW_BUILD_CSS` environment variable
**Environment Variables:**
- `RELDENS_ALLOW_RUN_BUNDLER` - Controls `createClientBundle()` execution (default: 0)
- `RELDENS_ALLOW_BUILD_CLIENT` - Controls `buildClient()` execution (default: 1)
- `RELDENS_ALLOW_BUILD_CSS` - Controls `buildCss()` execution (default: 1)
**Why this matters:** Production servers can regenerate clients and run Parcel builds for hot-reloading. These environment variables allow you to control when bundling happens, preventing unexpected builds during startup or deployment.
## Colyseus 0.16 - CRITICAL State Synchronization
**CRITICAL TIMING ISSUE**: Colyseus 0.16 state synchronization is asynchronous.
### Problem Pattern (WRONG)
```javascript
listenMessages(room, gameManager) {
if(!room.state || !room.state.bodies){
return false; // ❌ WRONG - callbacks never set up!
}
this.setAddBodyCallback(room, gameManager);
}
```
### Correct Pattern (RIGHT)
```javascript
listenMessages(room, gameManager) {
if(!room.state || !room.state.bodies){
room.onStateChange.once((state) => {
this.setAddBodyCallback(room, gameManager); // ✅ Wait for state
});
return false;
}
this.setAddBodyCallback(room, gameManager);
}
```
**Alternative - Use Reactive Patterns:**
```javascript
activateRoom(room) {
this.playersManager = RoomStateEntitiesManager.onEntityAdd(
room,
'players',
(player, key) => {
this.handlePlayerAdded(player, key);
}
);
}
```
**CRITICAL**: Colyseus auto-cleans all listeners. Never store manager references or add manual disposal code unless explicitly needed.
### Room Lifecycle
1. `onCreate(options)`: Initialize world, physics, objects
2. Player joins → `onJoin(client, options)`
3. Message handling → `onMessage(client, message)`
4. Player leaves → `onLeave(client, consented)`
5. Room disposal → `onDispose()`
## Common Development Patterns
### Adding a New Feature
1. Create feature module in `lib/{feature-name}/`
2. Add database table in `migrations/`
3. Create client/server subdirectories
4. Add feature entry to `features` table
5. Register in `lib/features/server/config-server.js`
6. Implement `setup()` method to hook events
### Modifying Game Logic
- **Combat/Skills**: Edit `lib/actions/server/battle.js` or `pve.js`
- **Player Stats**: Configure via database `stats` table
- **Room Behavior**: Extend `RoomScene` or hook `reldens.createRoomAfter` event
- **Client Rendering**: Modify Phaser scenes in `lib/game/client/scene-*.js`
### Working with Database
- Always use entity models via `dataServer.getEntity()`, never raw SQL
- Generated entities are read-only; extend in `server/models/`
- Use migrations for schema changes
- Regenerate entities after schema changes: `reldens generateEntities --override`
## Important Notes
- **Authoritative Server**: All game logic runs on server; client is display-only
- **Hot Plug**: Admin panel changes reload without restart if `RELDENS_HOT_PLUG=1`
- **Logging**: Use `@reldens/utils/Logger` (configurable via `RELDENS_LOG_LEVEL`)
- **File Operations**: Always use `@reldens/server-utils FileHandler` (never Node.js `fs`)
- **Shortcuts Class**: Import as `sc` from `@reldens/utils` - provides `sc.get`, `sc.hasOwn`, `sc.isArray`, etc.
- **Colyseus 0.16**: All client callbacks use `StateCallbacksManager` and `RoomStateEntitiesManager`
- **Buffer Polyfill**: Required for Parcel bundling with Colyseus 0.16
## Analysis Approach
When working on code issues:
- Always investigate thoroughly before making changes
- Read related files completely before proposing solutions
- Trace execution flows and dependencies
- Provide proof for issues, never guess or assume
- Verify file contents before creating patches
- A variable with an unexpected value is not an issue, it is the result of a previous issue
## Community & Support
- **Discord**: https://discord.gg/HuJMxUY
- **Demo**: https://dev.reldens.com/
- **Documentation**: https://www.reldens.com/documentation/installation
- **Issues**: https://github.com/damian-pastorini/reldens/issues
- **Contact**: info@dwdeveloper.com
## Detailed Reference Documentation
- **Commands**: `.claude/commands-reference.md` - All CLI commands
- **Environment Variables**: `.claude/environment-variables.md` - All RELDENS_* variables
- **Feature Modules**: `.claude/feature-modules.md` - All 23 feature modules
- **Storage Architecture**: `.claude/storage-architecture.md` - Entity management deep dive
- **Entities**: `.claude/entities-reference.md` - All 60+ entity types
- **Installer**: `.claude/installer-guide.md` - Web-based installation wizard guide
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Damian Alberto Pastorini
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
---
Join our Discord community!
---
# [Reldens - MMORPG Platform](https://www.reldens.com/)
Welcome to Reldens, a platform designed to empower developers in creating multiplayer games.
With a wide array of features, Reldens enables developers to craft a rich gaming experience without grappling with the complexities usually associated with making a multiplayer game secure and efficient over any network.
Built with popular development tools such as Node.js, Parcel, Colyseus, Phaser, and more, integrated with various database engines, and following robust development practices, the platform ensures security, ease of use, and high customization for developers.
While the current stage of the platform is tailored for developers, ongoing improvements to the administration panel aim to make game creation accessible to non-developers in the future.
---
## [Check our website for the latest news!](https://www.reldens.com/ "Check our website for the latest news")
---
## [Features Overview](https://www.reldens.com/features)
[First to mention, if the feature you need is not available, you can request a feature here: https://www.reldens.com/features-request](https://www.reldens.com/features-request)
As for the latest version released, the platform will provide you with the following features.
- Installation GUI: Easy to install through the web.
- Administration Panel: Manage every single aspect of your game through the panel.
- Automatic data generators and import commands for attributes, levels, maps, objects, etc.
- Trade System: Between Players (trade) and NPCs (buy and sell).
- Full In-game Chat: Global, by room, and private messages between users with chat types and split in tabs.
- Player Stats: Fully configurable stats! HP? MP? Magic? Stamina, you can set up as much as you need for your game logic.
- Items System: create all kinds of items (usable and equipment).
- Attacks, PVP, and PVE: create all kinds of skills, attacks, bullet type (can be dodged), target type (can't be dodged), fight with other players or just with NPCs.
- NPC's, Enemies, and Respawn Areas: Set up NPC's with different options to interact and create respawn areas.
- Teams and Clans System: Create, join, dismantle teams and clans, bonus based on clan level, and more.
- Drops and Rewards: NPCs can drop any kind of items (rewards), and it can be configured to be split among team members.
- Game rewards: you can configure rewards based on game events like daily or weekly login, it's events managed.
- Game scores: configurable games scores, for example, keep track on monster kills and get a global scores table view.
- Physics Engine and Pathfinder: Authoritative server with a physics engine and pathfinder.
- Gravity World: Set up your rooms with or without gravity to get totally different kinds of gameplay.
- Sounds System: Configurable multiple sound categories for any animation or scene.
- In-game Ads: Integrated with CrazyGames and GameMonetize to show ads in-game.
- Minimap: Optional configurable minimap.
- Configurable Players Name and Life-bars visibility.
- Terms and Conditions: Ready to be set up as you need.
- Guest Users.
- Users Registration: Continue playing later and double login invalidation.
- Multiple Players Creation.
- Registration and Login Integrated with Firebase.
- Multiple servers switch support, from room-A in server-A, to room-B in server-B without notice.
- Database Ready: With multiple drivers for different storage or using MySQL by default, all you need will be saved.
---
## [Installation](https://www.reldens.com/documentation/installation "Installation")
Please follow the Installation Guide: https://www.reldens.com/documentation/installation.
---
## [Demo](https://dev.reldens.com/)
We use this demo to show how many features are available.
To access you can register in the following link (the basic registration will require email, user, and password, but
none real data is required here):
- [https://demo.reldens.com/](https://dev.reldens.com/)
Your email is required to access the server admin:
- [https://demo.reldens.com/reldens-admin/](https://demo.reldens.com/reldens-admin/)
---
## Contact us
Join our Discord channel: [https://discord.gg/HuJMxUY](https://discord.gg/HuJMxUY) or contact us by email [info@dwdeveloper.com](mailto:info@dwdeveloper.com).
## Support us! :)
[](https://ko-fi.com/I2I81VISA)
[](https://www.patreon.com/bePatron?u=18074832)
If you like to contribute in any way or donate to support the project please also feel free to contact me at [info@dwdeveloper.com](mailto:info@dwdeveloper.com).
## Contributors
---
[](https://www.npmjs.com/package/reldens)
[](https://github.com/damian-pastorini/reldens)
[](https://discord.gg/HuJMxUY)
[](https://github.com/damian-pastorini/reldens)
---
## License
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fdamian-pastorini%2Freldens?ref=badge_large)
---
#### [By DwDeveloper](https://www.dwdeveloper.com/ "DwDeveloper")
================================================
FILE: bin/commander.js
================================================
#! /usr/bin/env node
/**
*
* Reldens - Commands
*
*/
const dotenv = require('dotenv');
const { spawn } = require('child_process');
const { CreateAdmin } = require('../lib/users/server/create-admin');
const { ResetPassword } = require('../lib/users/server/reset-password');
const { ThemeManager } = require('../lib/game/server/theme-manager');
const { PackagesInstallation } = require('../lib/game/server/installer/packages-installation');
const { ServerManager } = require('../server');
const { FileHandler } = require('@reldens/server-utils');
const { Logger, sc } = require('@reldens/utils');
class Commander
{
projectRoot = process.cwd();
reldensModulePath = FileHandler.joinPaths(this.projectRoot, 'node_modules', 'reldens');
projectThemeName = 'default';
jsSourceMaps = '1' === process.env.RELDENS_JS_SOURCEMAPS;
cssSourceMaps = '1' === process.env.RELDENS_CSS_SOURCEMAPS;
availableCommands = ['test', 'help', 'generateEntities', 'createAdmin', 'resetPassword'];
command = '';
prepareCommand()
{
if(!sc.hasOwn(process.env, 'RELDENS_LOG_LEVEL')){
process.env.RELDENS_LOG_LEVEL = 7;
}
Logger.info('- Reldens - ');
Logger.info('Use "help" as argument to see all the available commands:');
Logger.info('$ node scripts/reldens-commands.js help');
if(!FileHandler.exists(this.projectRoot)){
Logger.error('Can not access parent folder, check permissions.');
return false;
}
let parseResult = this.parseArgs();
if(!parseResult){
return false;
}
if(-1 !== this.availableCommands.indexOf(this.command)){
return true;
}
this.packagesInstallation = new PackagesInstallation({projectRoot: this.projectRoot});
if('createApp' === this.command){
if(!this.ensureReldensPackage()){
return false;
}
}
this.themeManager = new ThemeManager(this);
if(!this.validateThemeManagerCommand()){
return false;
}
this.themeManager.setupPaths(this);
Logger.info('Command "'+this.command+'" ready to be executed.');
Logger.info('Theme: '+this.projectThemeName);
return true;
}
ensureReldensPackage()
{
if(this.packagesInstallation.isPackageInstalled('reldens')){
return true;
}
Logger.info('Reldens package not found in node_modules.');
Logger.info('Installing reldens package...');
if(!this.packagesInstallation.processPackages(['reldens'], 'install')){
Logger.error('Failed to install reldens package.');
return false;
}
Logger.info('Reldens package installed successfully.');
return true;
}
parseArgs()
{
let args = process.argv;
if(2 === args.length){
Logger.error('Missing arguments.');
return false;
}
let extractedParams = args.slice(2);
this.command = extractedParams[0];
if(-1 !== this.availableCommands.indexOf(this.command)){
return true;
}
if(2 === extractedParams.length && '' !== extractedParams[1]){
this.projectThemeName = extractedParams[1];
}
return true;
}
validateThemeManagerCommand()
{
if(-1 !== this.availableCommands.indexOf(this.command)){
return true;
}
if('execute' === this.command || 'function' !== typeof this.themeManager[this.command]){
Logger.error('Invalid command:', this.command);
return false;
}
return true;
}
async execute()
{
await this.themeManager[this.command]();
Logger.info('Command executed!');
process.exit();
}
test()
{
let crudTestPath = FileHandler.joinPaths(this.projectRoot, 'crud-test');
FileHandler.createFolder(crudTestPath);
FileHandler.remove(crudTestPath);
Logger.info('Test OK.');
}
generateEntities()
{
this.loadEnvironmentConfig();
let args = [
'reldens-storage',
'generateEntities',
'--user='+process.env.RELDENS_DB_USER,
'--pass='+process.env.RELDENS_DB_PASSWORD,
'--host='+process.env.RELDENS_DB_HOST,
'--database='+process.env.RELDENS_DB_NAME,
'--driver='+(process.env.RELDENS_STORAGE_DRIVER || 'objection-js'),
'--client='+process.env.RELDENS_DB_CLIENT
];
let overrideArg = process.argv.find(arg => '--override' === arg);
if(overrideArg){
args.push('--override');
}
Logger.info('Running: npx '+args.join(' '));
let child = spawn('npx', args, {
stdio: 'inherit',
cwd: this.projectRoot,
shell: true
});
child.on('exit', (code) => {
process.exit(code || 0);
});
}
async createAdmin()
{
Logger.info('Creating admin user...');
let args = this.getCommandArgs(['user', 'pass', 'email']);
let serverManager = await this.initializeServerManager();
let service = new CreateAdmin(serverManager);
let result = await service.create(args.user, args.pass, args.email);
process.exit(result ? 0 : 1);
}
async resetPassword()
{
Logger.info('Resetting user password...');
let args = this.getCommandArgs(['user', 'pass']);
let serverManager = await this.initializeServerManager();
let service = new ResetPassword(serverManager);
let result = await service.reset(args.user, args.pass);
process.exit(result ? 0 : 1);
}
getCommandArgs(requiredArgs)
{
let args = process.argv.slice(2);
let parsedArgs = {};
for(let arg of args){
if(!arg.includes('=')){
continue;
}
let [key, value] = arg.split('=');
let cleanKey = key.replace('--', '');
parsedArgs[cleanKey] = value;
}
for(let requiredArg of requiredArgs){
if(!parsedArgs[requiredArg]){
Logger.error('Missing required argument: --'+requiredArg);
process.exit(1);
}
}
return parsedArgs;
}
loadEnvironmentConfig()
{
let envPath = FileHandler.joinPaths(this.projectRoot, '.env');
if(!FileHandler.exists(envPath)){
Logger.error('.env file not found at: '+envPath);
process.exit(1);
}
dotenv.config({path: envPath});
}
async initializeServerManager()
{
this.loadEnvironmentConfig();
let serverManager = new ServerManager({
projectRoot: this.projectRoot,
projectThemeName: this.projectThemeName
});
await serverManager.initializeStorage(serverManager.rawConfig, serverManager.dataServerDriver);
await serverManager.initializeConfigManager();
return serverManager;
}
help()
{
Logger.info(' - Available commands:'
+"\n"+'createApp - Create base project, copy all default files like in the skeleton.'
+"\n"+'resetDist - Delete and create the "dist" folder.'
+"\n"+'removeDist - Delete the "dist" folder.'
+"\n"+'installDefaultTheme - Copy theme and packages from node_modules into the current project theme.'
+"\n"+'copyAssetsToDist - Copy project theme assets into the "dist" folder.'
+"\n"+'copyKnexFile - Copy the knexfile.js sample into the project.'
+"\n"+'copyEnvFile - Copy the .env file sample into the project.'
+"\n"+'copyIndex - Copy the index file sample into the project.'
+"\n"+'copyDefaultAssets - Copy the reldens module default assets into the "dist/assets" folder.'
+"\n"+'copyDefaultTheme - Copy the reldens module default theme into the project theme.'
+"\n"+'copyPackage - Copy the reldens module packages into the project.'
+"\n"+'buildCss [theme-folder-name] - Builds the project theme styles.'
+"\n"+'buildClient [theme-folder-name] - Builds the project theme index.html.'
+"\n"+'buildSkeleton - Builds the styles and project theme index.html.'
+"\n"+'copyNew - Copy all default files for the fullRebuild.'
+"\n"+'fullRebuild - Rebuild the Skeleton from scratch.'
+"\n"+'installSkeleton - Installs Skeleton.'
+"\n"+'copyServerFiles - Reset the "dist" folder and runs a fullRebuild.'
+"\n"+'generateEntities [--override] - Generate entities from database using .env credentials.'
+"\n"+'createAdmin --user=X --pass=Y --email=Z - Create admin user with specified credentials.'
+"\n"+'resetPassword --user=X --pass=Y - Reset password for specified user.');
}
}
module.exports = new Commander();
================================================
FILE: bin/generate.js
================================================
#! /usr/bin/env node
const {
PlayersExperiencePerLevel,
MonstersExperiencePerLevel,
AttributesPerLevel
} = require('@reldens/game-data-generator');
const {
RandomMapGenerator,
LayerElementsObjectLoader,
LayerElementsCompositeLoader,
MultipleByLoaderGenerator,
MultipleWithAssociationsByLoaderGenerator
} = require('@reldens/tile-map-generator');
const { FileHandler } = require('@reldens/server-utils');
const { Logger } = require('@reldens/utils');
/**
*
* Commands:
*
* $ npx reldens-generate players-experience-per-level ./generate-data/players-experience-per-level.json
*
* $ npx reldens-generate monsters-experience-per-level ./generate-data/monsters-experience-per-level.json ./generate-data/players-level-sample.json
*
* $ npx reldens-generate attributes-per-level ./generate-data/attributes-per-level.json
*
* $ npx reldens-generate maps ./generate-data/map-data.json LayerElementsObjectLoader
*
* $ npx reldens-generate maps ./generate-data/map-composite-data.json LayerElementsCompositeLoader
*
* $ npx reldens-generate maps ./generate-data/map-composite-data-with-names.json MultipleByLoaderGenerator
*
* $ RELDENS_LOG_LEVEL=9 npx reldens-generate maps ./generate-data/map-composite-data-with-associations.json MultipleWithAssociationsByLoaderGenerator
*
*/
let mapsGenerateModes = {
LayerElementsObjectLoader: async (commandParams) => {
let loader = new LayerElementsObjectLoader(commandParams);
await loader.load();
let generator = new RandomMapGenerator(loader.mapData);
return await generator.generate();
},
LayerElementsCompositeLoader: async (commandParams) => {
let loader = new LayerElementsCompositeLoader(commandParams);
await loader.load();
let generator = new RandomMapGenerator();
await generator.fromElementsProvider(loader.mapData);
return await generator.generate();
},
MultipleByLoaderGenerator: async (commandParams) => {
let generator = new MultipleByLoaderGenerator({loaderData: commandParams});
await generator.generate();
},
MultipleWithAssociationsByLoaderGenerator: async (commandParams) => {
let generator = new MultipleWithAssociationsByLoaderGenerator({loaderData: commandParams});
await generator.generate();
}
};
let validCommands = {
'players-experience-per-level': (commandParams) => {
let playersExperiencePerLevel = new PlayersExperiencePerLevel(commandParams);
playersExperiencePerLevel.generate();
},
'monsters-experience-per-level': (commandParams) => {
let monstersExperiencePerLevel = new MonstersExperiencePerLevel(commandParams);
monstersExperiencePerLevel.generate();
},
'attributes-per-level': (commandParams) => {
let attributesPerLevel = new AttributesPerLevel(commandParams);
attributesPerLevel.generate();
},
'maps': async (commandParams) => {
if(!mapsGenerateModes[commandParams.importMode]){
console.error('- Invalid import mode. Valid options: '+Object.keys(mapsGenerateModes).join(', '));
}
let pathParts = commandParams.mapDataFile.split('/');
commandParams.mapDataFile = pathParts.pop();
commandParams.rootFolder = FileHandler.joinPaths(process.cwd(), ...pathParts);
// @TODO - BETA - Fix the generated folder placement.
// this will generate everything under rootFolder/whatever-the-path-is/generated:
await mapsGenerateModes[commandParams.importMode](commandParams);
let generatedFolder = FileHandler.joinPaths(commandParams.rootFolder, 'generated');
// we need to move the generated data to rootFolder/generated:
FileHandler.copyFolderSync(
generatedFolder,
FileHandler.joinPaths(process.cwd(), 'generated')
);
}
};
let args = process.argv;
if(2 === args.length){
console.error('- Missing arguments.', args);
return false;
}
let extractedParams = args.slice(2);
let command = extractedParams[0] || false;
if(!command){
console.error('- Missing command.');
return false;
}
if(-1 === Object.keys(validCommands).indexOf(command)){
console.error('- Invalid command.', command);
return false;
}
let importJson = 'monsters-experience-per-level' === command
|| 'players-experience-per-level' === command
|| 'attributes-per-level' === command;
if(importJson){
let filePath = FileHandler.joinPaths(process.cwd(), extractedParams[1] || '');
if(!filePath){
Logger.error('Invalid data file path.', process.cwd(), filePath);
return false;
}
let importedJson = FileHandler.fetchFileJson(filePath);
if(!importedJson){
console.error('- Can not parse data file.');
return false;
}
if('monsters-experience-per-level' === command){
let secondaryFilePath = FileHandler.joinPaths(process.cwd(), extractedParams[2] || '');
if(!secondaryFilePath){
Logger.error('Invalid data file path.', process.cwd(), secondaryFilePath);
return false;
}
let importedPlayerLevelsJson = FileHandler.fetchFileJson(secondaryFilePath);
if(!importedPlayerLevelsJson){
console.error('- Can not parse data file for player levels.');
return false;
}
importedJson.levelsExperienceByKey = importedPlayerLevelsJson;
}
return validCommands[command](importedJson);
}
if('maps' === command){
return validCommands[command]({
mapDataFile: extractedParams[1],
importMode: extractedParams[2] || ''
});
}
================================================
FILE: bin/import.js
================================================
#! /usr/bin/env node
const { ServerManager } = require('../server');
const { ObjectsImporter } = require('../lib/import/server/objects-importer');
const { PlayersExperiencePerLevelImporter } = require('../lib/import/server/players-experience-per-level-importer');
const { AttributesPerLevelImporter } = require('../lib/import/server/attributes-per-level-importer');
const { ClassPathsImporter } = require('../lib/import/server/class-paths-importer');
const { MapsImporter } = require('../lib/import/server/maps-importer');
const { SkillsImporter } = require('../lib/import/server/skills-importer');
const { FileHandler } = require('@reldens/server-utils');
/**
*
* Commands:
*
* $ npx reldens-import objects custom-game-theme-test generate-data/objects-generate-data.json
*
* - Player experience per-level import is not required if class-paths importer is going to be used.
* $ npx reldens-import players-experience-per-level custom-game-theme-test generate-data/players-experience-per-level.json
*
* - Class-paths importer will also import the experience per level.
* $ npx reldens-import class-paths custom-game-theme-test generate-data/class-paths.json
*
* $ npx reldens-import attributes-per-level custom-game-theme-test generate-data/class-paths-attributes-per-level.json
*
* $ npx reldens-import maps custom-game-theme-test generate-data/maps.json
*
* $ npx reldens-import skills custom-game-theme-test generate-data/skills-data.json
*
*/
let validCommands = {
'objects': async (data, projectThemeName) => {
let serverManager = await initializeServer(data, projectThemeName);
if(!serverManager){
return false;
}
let importer = new ObjectsImporter(serverManager);
await importer.import(data);
},
'players-experience-per-level': async (data, projectThemeName) => {
let serverManager = await initializeServer(data, projectThemeName);
if(!serverManager){
return false;
}
let importer = new PlayersExperiencePerLevelImporter(serverManager);
await importer.import(data);
},
'attributes-per-level': async (data, projectThemeName) => {
let serverManager = await initializeServer(data, projectThemeName);
if(!serverManager){
return false;
}
let importer = new AttributesPerLevelImporter(serverManager);
await importer.import(data);
},
'class-paths': async (data, projectThemeName) => {
let serverManager = await initializeServer(data, projectThemeName);
if(!serverManager){
return false;
}
let importer = new ClassPathsImporter(serverManager);
await importer.import(data);
},
'maps': async (data, projectThemeName) => {
let serverManager = await initializeServer(data, projectThemeName);
if(!serverManager){
return false;
}
let importer = new MapsImporter(serverManager);
await importer.import(data);
},
'skills': async (data, projectThemeName) => {
let serverManager = await initializeServer(data, projectThemeName);
if(!serverManager){
return false;
}
let importer = new SkillsImporter(serverManager);
await importer.import(data);
}
};
async function initializeServer(data, projectThemeName)
{
if (!data) {
console.error('- Missing data.', data);
return false;
}
let appServer = new ServerManager({
projectRoot: process.cwd(),
projectThemeName
});
await appServer.initializeStorage(appServer.rawConfig, appServer.dataServerDriver);
return appServer;
}
let args = process.argv;
if(2 === args.length){
console.error('- Missing arguments.', args);
return false;
}
let extractedParams = args.slice(2);
let command = extractedParams[0] || false;
if (!command) {
console.error('- Missing command.');
return false;
}
let themeName = extractedParams[1] || '';
if (!themeName) {
console.error('- Missing active theme name.');
return false;
}
if (-1 === Object.keys(validCommands).indexOf(command)) {
console.error('- Invalid command.', command);
return false;
}
validCommands[command](FileHandler.fetchFileJson(extractedParams[2] || ''), themeName).then(() => {
console.log('Done.');
process.exit();
}).catch((error) => {
console.error(error);
process.exit();
});
================================================
FILE: bin/install-test.js
================================================
#! /usr/bin/env node
/**
*
* Reldens - Install Test
*
*/
const commander = require('./commander');
commander.projectThemeName = 'custom-game-theme-test';
commander.themeManager.setupPaths(commander);
async function runCommander(commander) {
await commander.themeManager.installSkeleton();
process.exit();
}
runCommander(commander);
================================================
FILE: bin/reldens-commands.js
================================================
#! /usr/bin/env node
/**
*
* Reldens - Commands
*
*/
const commander = require('./commander');
if(commander.prepareCommand()){
-1 !== commander.availableCommands.indexOf(commander.command)
? commander[commander.command]()
: commander.execute().then(() => { console.info('- End'); });
}
================================================
FILE: client.js
================================================
/**
*
* Reldens - GameManager
*
*/
// transpile and polyfill:
require('core-js/stable');
require('regenerator-runtime/runtime');
const { GameManager } = require('./lib/game/client/game-manager');
module.exports.GameManager = GameManager;
================================================
FILE: generated-entities/entities/ads-banner-entity.js
================================================
/**
*
* Reldens - AdsBannerEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class AdsBannerEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
ads_id: {
type: 'reference',
reference: 'ads',
isRequired: true,
dbType: 'int'
},
banner_data: {
type: 'textarea',
isRequired: true,
dbType: 'text'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('banner_data'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.AdsBannerEntity = AdsBannerEntity;
================================================
FILE: generated-entities/entities/ads-entity.js
================================================
/**
*
* Reldens - AdsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class AdsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
provider_id: {
type: 'reference',
reference: 'ads_providers',
isRequired: true,
dbType: 'int'
},
type_id: {
type: 'reference',
reference: 'ads_types',
isRequired: true,
dbType: 'int'
},
width: {
type: 'number',
dbType: 'int'
},
height: {
type: 'number',
dbType: 'int'
},
position: {
dbType: 'varchar'
},
top: {
type: 'number',
dbType: 'int'
},
bottom: {
type: 'number',
dbType: 'int'
},
left: {
type: 'number',
dbType: 'int'
},
right: {
type: 'number',
dbType: 'int'
},
replay: {
type: 'number',
dbType: 'int'
},
enabled: {
type: 'boolean',
dbType: 'tinyint'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.AdsEntity = AdsEntity;
================================================
FILE: generated-entities/entities/ads-event-video-entity.js
================================================
/**
*
* Reldens - AdsEventVideoEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class AdsEventVideoEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
ads_id: {
type: 'reference',
reference: 'ads',
isRequired: true,
dbType: 'int'
},
event_key: {
isRequired: true,
dbType: 'varchar'
},
event_data: {
type: 'textarea',
isRequired: true,
dbType: 'text'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('event_data'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.AdsEventVideoEntity = AdsEventVideoEntity;
================================================
FILE: generated-entities/entities/ads-played-entity.js
================================================
/**
*
* Reldens - AdsPlayedEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class AdsPlayedEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
ads_id: {
type: 'reference',
reference: 'ads',
isRequired: true,
dbType: 'int'
},
player_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
},
started_at: {
type: 'datetime',
dbType: 'datetime'
},
ended_at: {
type: 'datetime',
dbType: 'datetime'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.AdsPlayedEntity = AdsPlayedEntity;
================================================
FILE: generated-entities/entities/ads-providers-entity.js
================================================
/**
*
* Reldens - AdsProvidersEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class AdsProvidersEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
enabled: {
type: 'boolean',
dbType: 'tinyint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.AdsProvidersEntity = AdsProvidersEntity;
================================================
FILE: generated-entities/entities/ads-types-entity.js
================================================
/**
*
* Reldens - AdsTypesEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class AdsTypesEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.AdsTypesEntity = AdsTypesEntity;
================================================
FILE: generated-entities/entities/audio-categories-entity.js
================================================
/**
*
* Reldens - AudioCategoriesEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class AudioCategoriesEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
category_key: {
isRequired: true,
dbType: 'varchar'
},
category_label: {
isRequired: true,
dbType: 'varchar'
},
enabled: {
type: 'boolean',
dbType: 'tinyint'
},
single_audio: {
type: 'boolean',
dbType: 'tinyint'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.AudioCategoriesEntity = AudioCategoriesEntity;
================================================
FILE: generated-entities/entities/audio-entity.js
================================================
/**
*
* Reldens - AudioEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class AudioEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
audio_key: {
isRequired: true,
dbType: 'varchar'
},
files_name: {
type: 'textarea',
isRequired: true,
dbType: 'text'
},
config: {
dbType: 'varchar'
},
room_id: {
type: 'reference',
reference: 'rooms',
dbType: 'int'
},
category_id: {
type: 'reference',
reference: 'audio_categories',
dbType: 'int'
},
enabled: {
type: 'boolean',
dbType: 'tinyint'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('files_name'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.AudioEntity = AudioEntity;
================================================
FILE: generated-entities/entities/audio-markers-entity.js
================================================
/**
*
* Reldens - AudioMarkersEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class AudioMarkersEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
audio_id: {
type: 'reference',
reference: 'audio',
isRequired: true,
dbType: 'int'
},
marker_key: {
isRequired: true,
dbType: 'varchar'
},
start: {
type: 'number',
isRequired: true,
dbType: 'int'
},
duration: {
type: 'number',
isRequired: true,
dbType: 'int'
},
config: {
type: 'textarea',
dbType: 'text'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('config'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.AudioMarkersEntity = AudioMarkersEntity;
================================================
FILE: generated-entities/entities/audio-player-config-entity.js
================================================
/**
*
* Reldens - AudioPlayerConfigEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class AudioPlayerConfigEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
player_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
},
category_id: {
type: 'reference',
reference: 'audio_categories',
dbType: 'int'
},
enabled: {
type: 'boolean',
dbType: 'tinyint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.AudioPlayerConfigEntity = AudioPlayerConfigEntity;
================================================
FILE: generated-entities/entities/chat-entity.js
================================================
/**
*
* Reldens - ChatEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ChatEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
player_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
},
room_id: {
type: 'reference',
reference: 'rooms',
dbType: 'int'
},
message: {
isRequired: true,
dbType: 'varchar'
},
private_player_id: {
type: 'reference',
reference: 'players',
dbType: 'int'
},
message_type: {
type: 'reference',
reference: 'chat_message_types',
dbType: 'int'
},
message_time: {
type: 'datetime',
isRequired: true,
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ChatEntity = ChatEntity;
================================================
FILE: generated-entities/entities/chat-message-types-entity.js
================================================
/**
*
* Reldens - ChatMessageTypesEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ChatMessageTypesEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
show_tab: {
type: 'boolean',
dbType: 'tinyint'
},
also_show_in_type: {
type: 'reference',
reference: 'chat_message_types',
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.ChatMessageTypesEntity = ChatMessageTypesEntity;
================================================
FILE: generated-entities/entities/clan-entity.js
================================================
/**
*
* Reldens - ClanEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class ClanEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'name';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
owner_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
points: {
type: 'number',
dbType: 'int'
},
level: {
type: 'reference',
reference: 'clan_levels',
isRequired: true,
dbType: 'int'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.ClanEntity = ClanEntity;
================================================
FILE: generated-entities/entities/clan-levels-entity.js
================================================
/**
*
* Reldens - ClanLevelsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ClanLevelsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
key: {
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
required_experience: {
type: 'number',
dbType: 'bigint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.ClanLevelsEntity = ClanLevelsEntity;
================================================
FILE: generated-entities/entities/clan-levels-modifiers-entity.js
================================================
/**
*
* Reldens - ClanLevelsModifiersEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ClanLevelsModifiersEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
level_id: {
type: 'reference',
reference: 'clan_levels',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
property_key: {
isRequired: true,
dbType: 'varchar'
},
operation: {
type: 'reference',
reference: 'operation_types',
isRequired: true,
dbType: 'int'
},
value: {
isRequired: true,
dbType: 'varchar'
},
minValue: {
dbType: 'varchar'
},
maxValue: {
dbType: 'varchar'
},
minProperty: {
dbType: 'varchar'
},
maxProperty: {
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.ClanLevelsModifiersEntity = ClanLevelsModifiersEntity;
================================================
FILE: generated-entities/entities/clan-members-entity.js
================================================
/**
*
* Reldens - ClanMembersEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ClanMembersEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
clan_id: {
type: 'reference',
reference: 'clan',
isRequired: true,
dbType: 'int'
},
player_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ClanMembersEntity = ClanMembersEntity;
================================================
FILE: generated-entities/entities/config-entity.js
================================================
/**
*
* Reldens - ConfigEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ConfigEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
scope: {
isRequired: true,
dbType: 'varchar'
},
path: {
isRequired: true,
dbType: 'varchar'
},
value: {
type: 'textarea',
isRequired: true,
dbType: 'text'
},
type: {
type: 'reference',
reference: 'config_types',
isRequired: true,
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('value'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ConfigEntity = ConfigEntity;
================================================
FILE: generated-entities/entities/config-types-entity.js
================================================
/**
*
* Reldens - ConfigTypesEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ConfigTypesEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.ConfigTypesEntity = ConfigTypesEntity;
================================================
FILE: generated-entities/entities/drops-animations-entity.js
================================================
/**
*
* Reldens - DropsAnimationsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class DropsAnimationsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
item_id: {
type: 'reference',
reference: 'items_item',
isRequired: true,
dbType: 'int'
},
asset_type: {
dbType: 'varchar'
},
asset_key: {
isRequired: true,
dbType: 'varchar'
},
file: {
isRequired: true,
dbType: 'varchar'
},
extra_params: {
type: 'textarea',
dbType: 'text'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('extra_params'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.DropsAnimationsEntity = DropsAnimationsEntity;
================================================
FILE: generated-entities/entities/features-entity.js
================================================
/**
*
* Reldens - FeaturesEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class FeaturesEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'title';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
code: {
isRequired: true,
dbType: 'varchar'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
is_enabled: {
type: 'boolean',
dbType: 'tinyint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.FeaturesEntity = FeaturesEntity;
================================================
FILE: generated-entities/entities/items-group-entity.js
================================================
/**
*
* Reldens - ItemsGroupEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class ItemsGroupEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
key: {
isRequired: true,
dbType: 'varchar'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
description: {
type: 'textarea',
dbType: 'text'
},
files_name: {
type: 'textarea',
dbType: 'text'
},
sort: {
type: 'number',
dbType: 'int'
},
items_limit: {
type: 'number',
dbType: 'int'
},
limit_per_item: {
type: 'number',
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = sc.removeFromArray([...propertiesKeys], ['description', 'files_name']);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.ItemsGroupEntity = ItemsGroupEntity;
================================================
FILE: generated-entities/entities/items-inventory-entity.js
================================================
/**
*
* Reldens - ItemsInventoryEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ItemsInventoryEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
owner_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
},
item_id: {
type: 'reference',
reference: 'items_item',
isRequired: true,
dbType: 'int'
},
qty: {
type: 'number',
dbType: 'int'
},
remaining_uses: {
type: 'number',
dbType: 'int'
},
is_active: {
type: 'boolean',
dbType: 'tinyint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ItemsInventoryEntity = ItemsInventoryEntity;
================================================
FILE: generated-entities/entities/items-item-entity.js
================================================
/**
*
* Reldens - ItemsItemEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class ItemsItemEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
key: {
isRequired: true,
dbType: 'varchar'
},
type: {
type: 'reference',
reference: 'items_types',
dbType: 'int'
},
group_id: {
type: 'reference',
reference: 'items_group',
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
description: {
dbType: 'varchar'
},
qty_limit: {
type: 'number',
dbType: 'int'
},
uses_limit: {
type: 'number',
dbType: 'int'
},
useTimeOut: {
type: 'number',
dbType: 'int'
},
execTimeOut: {
type: 'number',
dbType: 'int'
},
customData: {
type: 'textarea',
dbType: 'text'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('customData'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.ItemsItemEntity = ItemsItemEntity;
================================================
FILE: generated-entities/entities/items-item-modifiers-entity.js
================================================
/**
*
* Reldens - ItemsItemModifiersEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ItemsItemModifiersEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
item_id: {
type: 'reference',
reference: 'items_item',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
property_key: {
isRequired: true,
dbType: 'varchar'
},
operation: {
type: 'reference',
reference: 'operation_types',
isRequired: true,
dbType: 'int'
},
value: {
isRequired: true,
dbType: 'varchar'
},
maxProperty: {
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.ItemsItemModifiersEntity = ItemsItemModifiersEntity;
================================================
FILE: generated-entities/entities/items-types-entity.js
================================================
/**
*
* Reldens - ItemsTypesEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ItemsTypesEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.ItemsTypesEntity = ItemsTypesEntity;
================================================
FILE: generated-entities/entities/locale-entity.js
================================================
/**
*
* Reldens - LocaleEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class LocaleEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
locale: {
isRequired: true,
dbType: 'varchar'
},
language_code: {
isRequired: true,
dbType: 'varchar'
},
country_code: {
dbType: 'varchar'
},
enabled: {
type: 'boolean',
dbType: 'tinyint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.LocaleEntity = LocaleEntity;
================================================
FILE: generated-entities/entities/objects-animations-entity.js
================================================
/**
*
* Reldens - ObjectsAnimationsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ObjectsAnimationsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
object_id: {
type: 'reference',
reference: 'objects',
isRequired: true,
dbType: 'int'
},
animationKey: {
isRequired: true,
dbType: 'varchar'
},
animationData: {
type: 'textarea',
isRequired: true,
dbType: 'text'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('animationData'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ObjectsAnimationsEntity = ObjectsAnimationsEntity;
================================================
FILE: generated-entities/entities/objects-assets-entity.js
================================================
/**
*
* Reldens - ObjectsAssetsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ObjectsAssetsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
object_asset_id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
object_id: {
type: 'reference',
reference: 'objects',
isRequired: true,
dbType: 'int'
},
asset_type: {
isRequired: true,
dbType: 'varchar'
},
asset_key: {
isRequired: true,
dbType: 'varchar'
},
asset_file: {
isRequired: true,
dbType: 'varchar'
},
extra_params: {
type: 'textarea',
dbType: 'text'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('object_asset_id'), 1);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('extra_params'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ObjectsAssetsEntity = ObjectsAssetsEntity;
================================================
FILE: generated-entities/entities/objects-entity.js
================================================
/**
*
* Reldens - ObjectsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class ObjectsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'title';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
room_id: {
type: 'reference',
reference: 'rooms',
isRequired: true,
dbType: 'int'
},
layer_name: {
isRequired: true,
dbType: 'varchar'
},
tile_index: {
type: 'number',
dbType: 'int'
},
class_type: {
type: 'reference',
reference: 'objects_types',
dbType: 'int'
},
object_class_key: {
isRequired: true,
dbType: 'varchar'
},
client_key: {
type: 'textarea',
isRequired: true,
dbType: 'text'
},
[titleProperty]: {
dbType: 'varchar'
},
private_params: {
type: 'textarea',
dbType: 'text'
},
client_params: {
type: 'textarea',
dbType: 'text'
},
enabled: {
type: 'boolean',
dbType: 'tinyint'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = sc.removeFromArray([...propertiesKeys], ['client_key', 'private_params', 'client_params']);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.ObjectsEntity = ObjectsEntity;
================================================
FILE: generated-entities/entities/objects-items-inventory-entity.js
================================================
/**
*
* Reldens - ObjectsItemsInventoryEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ObjectsItemsInventoryEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
owner_id: {
type: 'reference',
reference: 'objects',
isRequired: true,
dbType: 'int'
},
item_id: {
type: 'reference',
reference: 'items_item',
isRequired: true,
dbType: 'int'
},
qty: {
type: 'number',
dbType: 'int'
},
remaining_uses: {
type: 'number',
dbType: 'int'
},
is_active: {
type: 'boolean',
dbType: 'tinyint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ObjectsItemsInventoryEntity = ObjectsItemsInventoryEntity;
================================================
FILE: generated-entities/entities/objects-items-requirements-entity.js
================================================
/**
*
* Reldens - ObjectsItemsRequirementsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ObjectsItemsRequirementsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
object_id: {
type: 'reference',
reference: 'objects',
isRequired: true,
dbType: 'int'
},
item_key: {
type: 'reference',
reference: 'items_item',
isRequired: true,
dbType: 'varchar'
},
required_item_key: {
type: 'reference',
reference: 'items_item',
isRequired: true,
dbType: 'varchar'
},
required_quantity: {
type: 'number',
dbType: 'int'
},
auto_remove_requirement: {
type: 'boolean',
dbType: 'tinyint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ObjectsItemsRequirementsEntity = ObjectsItemsRequirementsEntity;
================================================
FILE: generated-entities/entities/objects-items-rewards-entity.js
================================================
/**
*
* Reldens - ObjectsItemsRewardsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ObjectsItemsRewardsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
object_id: {
type: 'reference',
reference: 'objects',
isRequired: true,
dbType: 'int'
},
item_key: {
type: 'reference',
reference: 'items_item',
isRequired: true,
dbType: 'varchar'
},
reward_item_key: {
type: 'reference',
reference: 'items_item',
isRequired: true,
dbType: 'varchar'
},
reward_quantity: {
type: 'number',
dbType: 'int'
},
reward_item_is_required: {
type: 'boolean',
dbType: 'tinyint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ObjectsItemsRewardsEntity = ObjectsItemsRewardsEntity;
================================================
FILE: generated-entities/entities/objects-skills-entity.js
================================================
/**
*
* Reldens - ObjectsSkillsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ObjectsSkillsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
object_id: {
type: 'reference',
reference: 'objects',
isRequired: true,
dbType: 'int'
},
skill_id: {
type: 'reference',
reference: 'skills_skill',
isRequired: true,
dbType: 'int'
},
target_id: {
type: 'reference',
reference: 'target_options',
isRequired: true,
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ObjectsSkillsEntity = ObjectsSkillsEntity;
================================================
FILE: generated-entities/entities/objects-stats-entity.js
================================================
/**
*
* Reldens - ObjectsStatsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ObjectsStatsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
object_id: {
type: 'reference',
reference: 'objects',
isRequired: true,
dbType: 'int'
},
stat_id: {
type: 'reference',
reference: 'stats',
isRequired: true,
dbType: 'int'
},
base_value: {
type: 'number',
isRequired: true,
dbType: 'int'
},
value: {
type: 'number',
isRequired: true,
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ObjectsStatsEntity = ObjectsStatsEntity;
================================================
FILE: generated-entities/entities/objects-types-entity.js
================================================
/**
*
* Reldens - ObjectsTypesEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ObjectsTypesEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.ObjectsTypesEntity = ObjectsTypesEntity;
================================================
FILE: generated-entities/entities/operation-types-entity.js
================================================
/**
*
* Reldens - OperationTypesEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class OperationTypesEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
dbType: 'varchar'
},
key: {
type: 'number',
isRequired: true,
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.OperationTypesEntity = OperationTypesEntity;
================================================
FILE: generated-entities/entities/players-entity.js
================================================
/**
*
* Reldens - PlayersEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class PlayersEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'name';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
user_id: {
type: 'reference',
reference: 'users',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.PlayersEntity = PlayersEntity;
================================================
FILE: generated-entities/entities/players-state-entity.js
================================================
/**
*
* Reldens - PlayersStateEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class PlayersStateEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
player_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
},
room_id: {
type: 'reference',
reference: 'rooms',
isRequired: true,
dbType: 'int'
},
x: {
type: 'number',
isRequired: true,
dbType: 'int'
},
y: {
type: 'number',
isRequired: true,
dbType: 'int'
},
dir: {
isRequired: true,
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.PlayersStateEntity = PlayersStateEntity;
================================================
FILE: generated-entities/entities/players-stats-entity.js
================================================
/**
*
* Reldens - PlayersStatsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class PlayersStatsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
player_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
},
stat_id: {
type: 'reference',
reference: 'stats',
isRequired: true,
dbType: 'int'
},
base_value: {
type: 'number',
isRequired: true,
dbType: 'int'
},
value: {
type: 'number',
isRequired: true,
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.PlayersStatsEntity = PlayersStatsEntity;
================================================
FILE: generated-entities/entities/respawn-entity.js
================================================
/**
*
* Reldens - RespawnEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class RespawnEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
object_id: {
type: 'reference',
reference: 'objects',
isRequired: true,
dbType: 'int'
},
respawn_time: {
type: 'number',
dbType: 'int'
},
instances_limit: {
type: 'number',
dbType: 'int'
},
layer: {
isRequired: true,
dbType: 'varchar'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.RespawnEntity = RespawnEntity;
================================================
FILE: generated-entities/entities/rewards-entity.js
================================================
/**
*
* Reldens - RewardsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class RewardsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
object_id: {
type: 'reference',
reference: 'objects',
isRequired: true,
dbType: 'int'
},
item_id: {
type: 'reference',
reference: 'items_item',
dbType: 'int'
},
modifier_id: {
type: 'reference',
reference: 'rewards_modifiers',
dbType: 'int'
},
experience: {
type: 'number',
dbType: 'int'
},
drop_rate: {
type: 'number',
isRequired: true,
dbType: 'int'
},
drop_quantity: {
type: 'number',
isRequired: true,
dbType: 'int'
},
is_unique: {
type: 'boolean',
dbType: 'tinyint'
},
was_given: {
type: 'boolean',
dbType: 'tinyint'
},
has_drop_body: {
type: 'boolean',
dbType: 'tinyint'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.RewardsEntity = RewardsEntity;
================================================
FILE: generated-entities/entities/rewards-events-entity.js
================================================
/**
*
* Reldens - RewardsEventsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class RewardsEventsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
description: {
dbType: 'varchar'
},
handler_key: {
isRequired: true,
dbType: 'varchar'
},
event_key: {
isRequired: true,
dbType: 'varchar'
},
event_data: {
isRequired: true,
dbType: 'varchar'
},
position: {
type: 'number',
dbType: 'int'
},
enabled: {
type: 'boolean',
dbType: 'tinyint'
},
active_from: {
type: 'datetime',
dbType: 'datetime'
},
active_to: {
type: 'datetime',
dbType: 'datetime'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.RewardsEventsEntity = RewardsEventsEntity;
================================================
FILE: generated-entities/entities/rewards-events-state-entity.js
================================================
/**
*
* Reldens - RewardsEventsStateEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class RewardsEventsStateEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
rewards_events_id: {
type: 'reference',
reference: 'rewards_events',
isRequired: true,
dbType: 'int'
},
player_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
},
state: {
type: 'textarea',
dbType: 'text'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('state'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.RewardsEventsStateEntity = RewardsEventsStateEntity;
================================================
FILE: generated-entities/entities/rewards-modifiers-entity.js
================================================
/**
*
* Reldens - RewardsModifiersEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class RewardsModifiersEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
property_key: {
isRequired: true,
dbType: 'varchar'
},
operation: {
type: 'reference',
reference: 'operation_types',
isRequired: true,
dbType: 'int'
},
value: {
isRequired: true,
dbType: 'varchar'
},
minValue: {
dbType: 'varchar'
},
maxValue: {
dbType: 'varchar'
},
minProperty: {
dbType: 'varchar'
},
maxProperty: {
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.RewardsModifiersEntity = RewardsModifiersEntity;
================================================
FILE: generated-entities/entities/rooms-change-points-entity.js
================================================
/**
*
* Reldens - RoomsChangePointsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class RoomsChangePointsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
room_id: {
type: 'reference',
reference: 'rooms',
isRequired: true,
dbType: 'int'
},
tile_index: {
type: 'number',
isRequired: true,
dbType: 'int'
},
next_room_id: {
type: 'reference',
reference: 'rooms',
isRequired: true,
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.RoomsChangePointsEntity = RoomsChangePointsEntity;
================================================
FILE: generated-entities/entities/rooms-entity.js
================================================
/**
*
* Reldens - RoomsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class RoomsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'title';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
name: {
isRequired: true,
dbType: 'varchar'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
map_filename: {
isRequired: true,
dbType: 'varchar'
},
scene_images: {
isRequired: true,
dbType: 'varchar'
},
room_class_key: {
dbType: 'varchar'
},
server_url: {
dbType: 'varchar'
},
customData: {
type: 'textarea',
dbType: 'text'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('customData'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.RoomsEntity = RoomsEntity;
================================================
FILE: generated-entities/entities/rooms-return-points-entity.js
================================================
/**
*
* Reldens - RoomsReturnPointsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class RoomsReturnPointsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
room_id: {
type: 'reference',
reference: 'rooms',
isRequired: true,
dbType: 'int'
},
direction: {
isRequired: true,
dbType: 'varchar'
},
x: {
type: 'number',
isRequired: true,
dbType: 'int'
},
y: {
type: 'number',
isRequired: true,
dbType: 'int'
},
is_default: {
type: 'boolean',
dbType: 'tinyint'
},
from_room_id: {
type: 'reference',
reference: 'rooms',
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.RoomsReturnPointsEntity = RoomsReturnPointsEntity;
================================================
FILE: generated-entities/entities/scores-detail-entity.js
================================================
/**
*
* Reldens - ScoresDetailEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class ScoresDetailEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
player_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
},
obtained_score: {
type: 'number',
isRequired: true,
dbType: 'int'
},
kill_time: {
type: 'datetime',
dbType: 'datetime'
},
kill_player_id: {
type: 'number',
dbType: 'int'
},
kill_npc_id: {
type: 'number',
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ScoresDetailEntity = ScoresDetailEntity;
================================================
FILE: generated-entities/entities/scores-entity.js
================================================
/**
*
* Reldens - ScoresEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class ScoresEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
player_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
},
total_score: {
type: 'number',
isRequired: true,
dbType: 'int'
},
players_kills_count: {
type: 'number',
isRequired: true,
dbType: 'int'
},
npcs_kills_count: {
type: 'number',
isRequired: true,
dbType: 'int'
},
last_player_kill_time: {
type: 'datetime',
dbType: 'datetime'
},
last_npc_kill_time: {
type: 'datetime',
dbType: 'datetime'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.ScoresEntity = ScoresEntity;
================================================
FILE: generated-entities/entities/skills-class-level-up-animations-entity.js
================================================
/**
*
* Reldens - SkillsClassLevelUpAnimationsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsClassLevelUpAnimationsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
class_path_id: {
type: 'reference',
reference: 'skills_class_path',
dbType: 'int'
},
level_id: {
type: 'reference',
reference: 'skills_levels',
dbType: 'int'
},
animationData: {
type: 'textarea',
isRequired: true,
dbType: 'text'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('animationData'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.SkillsClassLevelUpAnimationsEntity = SkillsClassLevelUpAnimationsEntity;
================================================
FILE: generated-entities/entities/skills-class-path-entity.js
================================================
/**
*
* Reldens - SkillsClassPathEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class SkillsClassPathEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
key: {
isRequired: true,
dbType: 'varchar'
},
[titleProperty]: {
dbType: 'varchar'
},
levels_set_id: {
type: 'reference',
reference: 'skills_levels_set',
isRequired: true,
dbType: 'int'
},
enabled: {
type: 'boolean',
dbType: 'tinyint'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsClassPathEntity = SkillsClassPathEntity;
================================================
FILE: generated-entities/entities/skills-class-path-level-labels-entity.js
================================================
/**
*
* Reldens - SkillsClassPathLevelLabelsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsClassPathLevelLabelsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
class_path_id: {
type: 'reference',
reference: 'skills_class_path',
isRequired: true,
dbType: 'int'
},
level_id: {
type: 'reference',
reference: 'skills_levels',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsClassPathLevelLabelsEntity = SkillsClassPathLevelLabelsEntity;
================================================
FILE: generated-entities/entities/skills-class-path-level-skills-entity.js
================================================
/**
*
* Reldens - SkillsClassPathLevelSkillsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsClassPathLevelSkillsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
class_path_id: {
type: 'reference',
reference: 'skills_class_path',
isRequired: true,
dbType: 'int'
},
level_id: {
type: 'reference',
reference: 'skills_levels',
isRequired: true,
dbType: 'int'
},
skill_id: {
type: 'reference',
reference: 'skills_skill',
isRequired: true,
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.SkillsClassPathLevelSkillsEntity = SkillsClassPathLevelSkillsEntity;
================================================
FILE: generated-entities/entities/skills-groups-entity.js
================================================
/**
*
* Reldens - SkillsGroupsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsGroupsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
key: {
isRequired: true,
dbType: 'varchar'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
description: {
isRequired: true,
dbType: 'varchar'
},
sort: {
type: 'number',
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsGroupsEntity = SkillsGroupsEntity;
================================================
FILE: generated-entities/entities/skills-levels-entity.js
================================================
/**
*
* Reldens - SkillsLevelsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsLevelsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
key: {
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
required_experience: {
type: 'number',
dbType: 'bigint'
},
level_set_id: {
type: 'reference',
reference: 'skills_levels_set',
isRequired: true,
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsLevelsEntity = SkillsLevelsEntity;
================================================
FILE: generated-entities/entities/skills-levels-modifiers-conditions-entity.js
================================================
/**
*
* Reldens - SkillsLevelsModifiersConditionsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsLevelsModifiersConditionsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
levels_modifier_id: {
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
property_key: {
isRequired: true,
dbType: 'varchar'
},
conditional: {
availableValues: [
{value: 1, label: 'eq'},
{value: 2, label: 'ne'},
{value: 3, label: 'lt'},
{value: 4, label: 'gt'},
{value: 5, label: 'le'},
{value: 6, label: 'ge'}
],
isRequired: true,
dbType: 'enum'
},
value: {
isRequired: true,
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsLevelsModifiersConditionsEntity = SkillsLevelsModifiersConditionsEntity;
================================================
FILE: generated-entities/entities/skills-levels-modifiers-entity.js
================================================
/**
*
* Reldens - SkillsLevelsModifiersEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsLevelsModifiersEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
level_id: {
type: 'reference',
reference: 'skills_levels',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
property_key: {
isRequired: true,
dbType: 'varchar'
},
operation: {
type: 'reference',
reference: 'operation_types',
isRequired: true,
dbType: 'int'
},
value: {
isRequired: true,
dbType: 'varchar'
},
minValue: {
dbType: 'varchar'
},
maxValue: {
dbType: 'varchar'
},
minProperty: {
dbType: 'varchar'
},
maxProperty: {
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsLevelsModifiersEntity = SkillsLevelsModifiersEntity;
================================================
FILE: generated-entities/entities/skills-levels-set-entity.js
================================================
/**
*
* Reldens - SkillsLevelsSetEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class SkillsLevelsSetEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
key: {
dbType: 'varchar'
},
[titleProperty]: {
dbType: 'varchar'
},
autoFillRanges: {
type: 'boolean',
dbType: 'tinyint'
},
autoFillExperienceMultiplier: {
type: 'number',
dbType: 'int'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsLevelsSetEntity = SkillsLevelsSetEntity;
================================================
FILE: generated-entities/entities/skills-owners-class-path-entity.js
================================================
/**
*
* Reldens - SkillsOwnersClassPathEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsOwnersClassPathEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
class_path_id: {
type: 'reference',
reference: 'skills_class_path',
isRequired: true,
dbType: 'int'
},
owner_id: {
type: 'reference',
reference: 'players',
isRequired: true,
dbType: 'int'
},
currentLevel: {
type: 'number',
dbType: 'bigint'
},
currentExp: {
type: 'number',
dbType: 'bigint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.SkillsOwnersClassPathEntity = SkillsOwnersClassPathEntity;
================================================
FILE: generated-entities/entities/skills-skill-animations-entity.js
================================================
/**
*
* Reldens - SkillsSkillAnimationsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsSkillAnimationsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
skill_id: {
type: 'reference',
reference: 'skills_skill',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
classKey: {
dbType: 'varchar'
},
animationData: {
type: 'textarea',
isRequired: true,
dbType: 'text'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('animationData'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsSkillAnimationsEntity = SkillsSkillAnimationsEntity;
================================================
FILE: generated-entities/entities/skills-skill-attack-entity.js
================================================
/**
*
* Reldens - SkillsSkillAttackEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class SkillsSkillAttackEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
skill_id: {
type: 'reference',
reference: 'skills_skill',
isRequired: true,
dbType: 'int'
},
affectedProperty: {
isRequired: true,
dbType: 'varchar'
},
allowEffectBelowZero: {
type: 'boolean',
dbType: 'tinyint'
},
hitDamage: {
type: 'number',
isRequired: true,
dbType: 'int'
},
applyDirectDamage: {
type: 'boolean',
dbType: 'tinyint'
},
attackProperties: {
type: 'textarea',
dbType: 'text'
},
defenseProperties: {
type: 'textarea',
dbType: 'text'
},
aimProperties: {
type: 'textarea',
dbType: 'text'
},
dodgeProperties: {
type: 'textarea',
dbType: 'text'
},
dodgeFullEnabled: {
type: 'boolean',
dbType: 'tinyint'
},
dodgeOverAimSuccess: {
type: 'boolean',
dbType: 'tinyint'
},
damageAffected: {
type: 'boolean',
dbType: 'tinyint'
},
criticalAffected: {
type: 'boolean',
dbType: 'tinyint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = sc.removeFromArray([...propertiesKeys], ['attackProperties', 'defenseProperties', 'aimProperties', 'dodgeProperties']);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.SkillsSkillAttackEntity = SkillsSkillAttackEntity;
================================================
FILE: generated-entities/entities/skills-skill-entity.js
================================================
/**
*
* Reldens - SkillsSkillEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class SkillsSkillEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
key: {
isRequired: true,
dbType: 'varchar'
},
type: {
type: 'reference',
reference: 'skills_skill_type',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
dbType: 'varchar'
},
autoValidation: {
type: 'boolean',
dbType: 'tinyint'
},
skillDelay: {
type: 'number',
isRequired: true,
dbType: 'int'
},
castTime: {
type: 'number',
isRequired: true,
dbType: 'int'
},
usesLimit: {
type: 'number',
dbType: 'int'
},
range: {
type: 'number',
isRequired: true,
dbType: 'int'
},
rangeAutomaticValidation: {
type: 'boolean',
dbType: 'tinyint'
},
rangePropertyX: {
isRequired: true,
dbType: 'varchar'
},
rangePropertyY: {
isRequired: true,
dbType: 'varchar'
},
rangeTargetPropertyX: {
dbType: 'varchar'
},
rangeTargetPropertyY: {
dbType: 'varchar'
},
allowSelfTarget: {
type: 'boolean',
dbType: 'tinyint'
},
criticalChance: {
type: 'number',
dbType: 'int'
},
criticalMultiplier: {
type: 'number',
dbType: 'int'
},
criticalFixedValue: {
type: 'number',
dbType: 'int'
},
customData: {
type: 'textarea',
dbType: 'text'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('customData'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsSkillEntity = SkillsSkillEntity;
================================================
FILE: generated-entities/entities/skills-skill-group-relation-entity.js
================================================
/**
*
* Reldens - SkillsSkillGroupRelationEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsSkillGroupRelationEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
skill_id: {
type: 'reference',
reference: 'skills_skill',
isRequired: true,
dbType: 'int'
},
group_id: {
type: 'reference',
reference: 'skills_groups',
isRequired: true,
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.SkillsSkillGroupRelationEntity = SkillsSkillGroupRelationEntity;
================================================
FILE: generated-entities/entities/skills-skill-owner-conditions-entity.js
================================================
/**
*
* Reldens - SkillsSkillOwnerConditionsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsSkillOwnerConditionsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
skill_id: {
type: 'reference',
reference: 'skills_skill',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
property_key: {
isRequired: true,
dbType: 'varchar'
},
conditional: {
availableValues: [
{value: 1, label: 'eq'},
{value: 2, label: 'ne'},
{value: 3, label: 'lt'},
{value: 4, label: 'gt'},
{value: 5, label: 'le'},
{value: 6, label: 'ge'}
],
isRequired: true,
dbType: 'enum'
},
value: {
isRequired: true,
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsSkillOwnerConditionsEntity = SkillsSkillOwnerConditionsEntity;
================================================
FILE: generated-entities/entities/skills-skill-owner-effects-conditions-entity.js
================================================
/**
*
* Reldens - SkillsSkillOwnerEffectsConditionsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsSkillOwnerEffectsConditionsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
skill_owner_effect_id: {
type: 'reference',
reference: 'skills_skill_owner_effects',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
property_key: {
isRequired: true,
dbType: 'varchar'
},
conditional: {
availableValues: [
{value: 1, label: 'eq'},
{value: 2, label: 'ne'},
{value: 3, label: 'lt'},
{value: 4, label: 'gt'},
{value: 5, label: 'le'},
{value: 6, label: 'ge'}
],
isRequired: true,
dbType: 'enum'
},
value: {
isRequired: true,
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsSkillOwnerEffectsConditionsEntity = SkillsSkillOwnerEffectsConditionsEntity;
================================================
FILE: generated-entities/entities/skills-skill-owner-effects-entity.js
================================================
/**
*
* Reldens - SkillsSkillOwnerEffectsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsSkillOwnerEffectsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
skill_id: {
type: 'reference',
reference: 'skills_skill',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
property_key: {
isRequired: true,
dbType: 'varchar'
},
operation: {
type: 'reference',
reference: 'operation_types',
isRequired: true,
dbType: 'int'
},
value: {
isRequired: true,
dbType: 'varchar'
},
minValue: {
isRequired: true,
dbType: 'varchar'
},
maxValue: {
isRequired: true,
dbType: 'varchar'
},
minProperty: {
dbType: 'varchar'
},
maxProperty: {
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsSkillOwnerEffectsEntity = SkillsSkillOwnerEffectsEntity;
================================================
FILE: generated-entities/entities/skills-skill-physical-data-entity.js
================================================
/**
*
* Reldens - SkillsSkillPhysicalDataEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsSkillPhysicalDataEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
skill_id: {
type: 'reference',
reference: 'skills_skill',
isRequired: true,
dbType: 'int'
},
magnitude: {
type: 'number',
isRequired: true,
dbType: 'int'
},
objectWidth: {
type: 'number',
isRequired: true,
dbType: 'int'
},
objectHeight: {
type: 'number',
isRequired: true,
dbType: 'int'
},
validateTargetOnHit: {
type: 'boolean',
dbType: 'tinyint'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.SkillsSkillPhysicalDataEntity = SkillsSkillPhysicalDataEntity;
================================================
FILE: generated-entities/entities/skills-skill-target-effects-conditions-entity.js
================================================
/**
*
* Reldens - SkillsSkillTargetEffectsConditionsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsSkillTargetEffectsConditionsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
skill_target_effect_id: {
type: 'reference',
reference: 'skills_skill_target_effects',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
property_key: {
isRequired: true,
dbType: 'varchar'
},
conditional: {
availableValues: [
{value: 1, label: 'eq'},
{value: 2, label: 'ne'},
{value: 3, label: 'lt'},
{value: 4, label: 'gt'},
{value: 5, label: 'le'},
{value: 6, label: 'ge'}
],
isRequired: true,
dbType: 'enum'
},
value: {
isRequired: true,
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsSkillTargetEffectsConditionsEntity = SkillsSkillTargetEffectsConditionsEntity;
================================================
FILE: generated-entities/entities/skills-skill-target-effects-entity.js
================================================
/**
*
* Reldens - SkillsSkillTargetEffectsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsSkillTargetEffectsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
skill_id: {
type: 'reference',
reference: 'skills_skill',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
property_key: {
isRequired: true,
dbType: 'varchar'
},
operation: {
type: 'reference',
reference: 'operation_types',
isRequired: true,
dbType: 'int'
},
value: {
isRequired: true,
dbType: 'varchar'
},
minValue: {
isRequired: true,
dbType: 'varchar'
},
maxValue: {
isRequired: true,
dbType: 'varchar'
},
minProperty: {
dbType: 'varchar'
},
maxProperty: {
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsSkillTargetEffectsEntity = SkillsSkillTargetEffectsEntity;
================================================
FILE: generated-entities/entities/skills-skill-type-entity.js
================================================
/**
*
* Reldens - SkillsSkillTypeEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class SkillsSkillTypeEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SkillsSkillTypeEntity = SkillsSkillTypeEntity;
================================================
FILE: generated-entities/entities/snippets-entity.js
================================================
/**
*
* Reldens - SnippetsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class SnippetsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'key';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
locale_id: {
type: 'reference',
reference: 'locale',
isRequired: true,
dbType: 'int'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
value: {
type: 'textarea',
isRequired: true,
dbType: 'text'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('value'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.SnippetsEntity = SnippetsEntity;
================================================
FILE: generated-entities/entities/stats-entity.js
================================================
/**
*
* Reldens - StatsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class StatsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let titleProperty = 'label';
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
key: {
isRequired: true,
dbType: 'varchar'
},
[titleProperty]: {
isRequired: true,
dbType: 'varchar'
},
description: {
isRequired: true,
dbType: 'varchar'
},
base_value: {
type: 'number',
isRequired: true,
dbType: 'int'
},
customData: {
type: 'textarea',
dbType: 'text'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('customData'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps
};
}
}
module.exports.StatsEntity = StatsEntity;
================================================
FILE: generated-entities/entities/target-options-entity.js
================================================
/**
*
* Reldens - TargetOptionsEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class TargetOptionsEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
target_key: {
isRequired: true,
dbType: 'varchar'
},
target_label: {
dbType: 'varchar'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.TargetOptionsEntity = TargetOptionsEntity;
================================================
FILE: generated-entities/entities/users-entity.js
================================================
/**
*
* Reldens - UsersEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
const { sc } = require('@reldens/utils');
class UsersEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
email: {
isRequired: true,
dbType: 'varchar'
},
username: {
isRequired: true,
dbType: 'varchar'
},
password: {
isRequired: true,
dbType: 'varchar'
},
role_id: {
type: 'number',
isRequired: true,
dbType: 'int'
},
status: {
isRequired: true,
dbType: 'varchar'
},
created_at: {
type: 'datetime',
dbType: 'timestamp'
},
updated_at: {
type: 'datetime',
dbType: 'timestamp'
},
played_time: {
type: 'number',
dbType: 'int'
},
login_count: {
type: 'number',
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = [...propertiesKeys];
showProperties.splice(showProperties.indexOf('password'), 1);
let editProperties = sc.removeFromArray([...propertiesKeys], ['id', 'created_at', 'updated_at']);
let listProperties = [...propertiesKeys];
listProperties.splice(listProperties.indexOf('password'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.UsersEntity = UsersEntity;
================================================
FILE: generated-entities/entities/users-locale-entity.js
================================================
/**
*
* Reldens - UsersLocaleEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class UsersLocaleEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
locale_id: {
type: 'reference',
reference: 'locale',
dbType: 'int'
},
user_id: {
type: 'reference',
reference: 'users',
dbType: 'int'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.UsersLocaleEntity = UsersLocaleEntity;
================================================
FILE: generated-entities/entities/users-login-entity.js
================================================
/**
*
* Reldens - UsersLoginEntity
*
*/
const { EntityProperties } = require('@reldens/storage');
class UsersLoginEntity extends EntityProperties
{
static propertiesConfig(extraProps)
{
let properties = {
id: {
isId: true,
type: 'number',
isRequired: true,
dbType: 'int'
},
user_id: {
type: 'reference',
reference: 'users',
isRequired: true,
dbType: 'int'
},
login_date: {
type: 'datetime',
dbType: 'timestamp'
},
logout_date: {
type: 'datetime',
dbType: 'timestamp'
}
};
let propertiesKeys = Object.keys(properties);
let showProperties = propertiesKeys;
let editProperties = [...propertiesKeys];
editProperties.splice(editProperties.indexOf('id'), 1);
let listProperties = propertiesKeys;
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
...extraProps
};
}
}
module.exports.UsersLoginEntity = UsersLoginEntity;
================================================
FILE: generated-entities/entities-config.js
================================================
/**
*
* Reldens - Entities Config
*
*/
const { AdsBannerEntity } = require('./entities/ads-banner-entity');
const { AdsEntity } = require('./entities/ads-entity');
const { AdsEventVideoEntity } = require('./entities/ads-event-video-entity');
const { AdsPlayedEntity } = require('./entities/ads-played-entity');
const { AdsProvidersEntity } = require('./entities/ads-providers-entity');
const { AdsTypesEntity } = require('./entities/ads-types-entity');
const { AudioCategoriesEntity } = require('./entities/audio-categories-entity');
const { AudioEntity } = require('./entities/audio-entity');
const { AudioMarkersEntity } = require('./entities/audio-markers-entity');
const { AudioPlayerConfigEntity } = require('./entities/audio-player-config-entity');
const { ChatEntity } = require('./entities/chat-entity');
const { ChatMessageTypesEntity } = require('./entities/chat-message-types-entity');
const { ClanEntity } = require('./entities/clan-entity');
const { ClanLevelsEntity } = require('./entities/clan-levels-entity');
const { ClanLevelsModifiersEntity } = require('./entities/clan-levels-modifiers-entity');
const { ClanMembersEntity } = require('./entities/clan-members-entity');
const { ConfigEntity } = require('./entities/config-entity');
const { ConfigTypesEntity } = require('./entities/config-types-entity');
const { DropsAnimationsEntity } = require('./entities/drops-animations-entity');
const { FeaturesEntity } = require('./entities/features-entity');
const { ItemsGroupEntity } = require('./entities/items-group-entity');
const { ItemsInventoryEntity } = require('./entities/items-inventory-entity');
const { ItemsItemEntity } = require('./entities/items-item-entity');
const { ItemsItemModifiersEntity } = require('./entities/items-item-modifiers-entity');
const { ItemsTypesEntity } = require('./entities/items-types-entity');
const { LocaleEntity } = require('./entities/locale-entity');
const { ObjectsAnimationsEntity } = require('./entities/objects-animations-entity');
const { ObjectsAssetsEntity } = require('./entities/objects-assets-entity');
const { ObjectsEntity } = require('./entities/objects-entity');
const { ObjectsItemsInventoryEntity } = require('./entities/objects-items-inventory-entity');
const { ObjectsItemsRequirementsEntity } = require('./entities/objects-items-requirements-entity');
const { ObjectsItemsRewardsEntity } = require('./entities/objects-items-rewards-entity');
const { ObjectsSkillsEntity } = require('./entities/objects-skills-entity');
const { ObjectsStatsEntity } = require('./entities/objects-stats-entity');
const { ObjectsTypesEntity } = require('./entities/objects-types-entity');
const { OperationTypesEntity } = require('./entities/operation-types-entity');
const { PlayersEntity } = require('./entities/players-entity');
const { PlayersStateEntity } = require('./entities/players-state-entity');
const { PlayersStatsEntity } = require('./entities/players-stats-entity');
const { RespawnEntity } = require('./entities/respawn-entity');
const { RewardsEntity } = require('./entities/rewards-entity');
const { RewardsEventsEntity } = require('./entities/rewards-events-entity');
const { RewardsEventsStateEntity } = require('./entities/rewards-events-state-entity');
const { RewardsModifiersEntity } = require('./entities/rewards-modifiers-entity');
const { RoomsChangePointsEntity } = require('./entities/rooms-change-points-entity');
const { RoomsEntity } = require('./entities/rooms-entity');
const { RoomsReturnPointsEntity } = require('./entities/rooms-return-points-entity');
const { ScoresDetailEntity } = require('./entities/scores-detail-entity');
const { ScoresEntity } = require('./entities/scores-entity');
const { SkillsClassLevelUpAnimationsEntity } = require('./entities/skills-class-level-up-animations-entity');
const { SkillsClassPathEntity } = require('./entities/skills-class-path-entity');
const { SkillsClassPathLevelLabelsEntity } = require('./entities/skills-class-path-level-labels-entity');
const { SkillsClassPathLevelSkillsEntity } = require('./entities/skills-class-path-level-skills-entity');
const { SkillsGroupsEntity } = require('./entities/skills-groups-entity');
const { SkillsLevelsEntity } = require('./entities/skills-levels-entity');
const { SkillsLevelsModifiersConditionsEntity } = require('./entities/skills-levels-modifiers-conditions-entity');
const { SkillsLevelsModifiersEntity } = require('./entities/skills-levels-modifiers-entity');
const { SkillsLevelsSetEntity } = require('./entities/skills-levels-set-entity');
const { SkillsOwnersClassPathEntity } = require('./entities/skills-owners-class-path-entity');
const { SkillsSkillAnimationsEntity } = require('./entities/skills-skill-animations-entity');
const { SkillsSkillAttackEntity } = require('./entities/skills-skill-attack-entity');
const { SkillsSkillEntity } = require('./entities/skills-skill-entity');
const { SkillsSkillGroupRelationEntity } = require('./entities/skills-skill-group-relation-entity');
const { SkillsSkillOwnerConditionsEntity } = require('./entities/skills-skill-owner-conditions-entity');
const { SkillsSkillOwnerEffectsConditionsEntity } = require('./entities/skills-skill-owner-effects-conditions-entity');
const { SkillsSkillOwnerEffectsEntity } = require('./entities/skills-skill-owner-effects-entity');
const { SkillsSkillPhysicalDataEntity } = require('./entities/skills-skill-physical-data-entity');
const { SkillsSkillTargetEffectsConditionsEntity } = require('./entities/skills-skill-target-effects-conditions-entity');
const { SkillsSkillTargetEffectsEntity } = require('./entities/skills-skill-target-effects-entity');
const { SkillsSkillTypeEntity } = require('./entities/skills-skill-type-entity');
const { SnippetsEntity } = require('./entities/snippets-entity');
const { StatsEntity } = require('./entities/stats-entity');
const { TargetOptionsEntity } = require('./entities/target-options-entity');
const { UsersEntity } = require('./entities/users-entity');
const { UsersLocaleEntity } = require('./entities/users-locale-entity');
const { UsersLoginEntity } = require('./entities/users-login-entity');
let entitiesConfig = {
adsBanner: AdsBannerEntity.propertiesConfig(),
ads: AdsEntity.propertiesConfig(),
adsEventVideo: AdsEventVideoEntity.propertiesConfig(),
adsPlayed: AdsPlayedEntity.propertiesConfig(),
adsProviders: AdsProvidersEntity.propertiesConfig(),
adsTypes: AdsTypesEntity.propertiesConfig(),
audioCategories: AudioCategoriesEntity.propertiesConfig(),
audio: AudioEntity.propertiesConfig(),
audioMarkers: AudioMarkersEntity.propertiesConfig(),
audioPlayerConfig: AudioPlayerConfigEntity.propertiesConfig(),
chat: ChatEntity.propertiesConfig(),
chatMessageTypes: ChatMessageTypesEntity.propertiesConfig(),
clan: ClanEntity.propertiesConfig(),
clanLevels: ClanLevelsEntity.propertiesConfig(),
clanLevelsModifiers: ClanLevelsModifiersEntity.propertiesConfig(),
clanMembers: ClanMembersEntity.propertiesConfig(),
config: ConfigEntity.propertiesConfig(),
configTypes: ConfigTypesEntity.propertiesConfig(),
dropsAnimations: DropsAnimationsEntity.propertiesConfig(),
features: FeaturesEntity.propertiesConfig(),
itemsGroup: ItemsGroupEntity.propertiesConfig(),
itemsInventory: ItemsInventoryEntity.propertiesConfig(),
itemsItem: ItemsItemEntity.propertiesConfig(),
itemsItemModifiers: ItemsItemModifiersEntity.propertiesConfig(),
itemsTypes: ItemsTypesEntity.propertiesConfig(),
locale: LocaleEntity.propertiesConfig(),
objectsAnimations: ObjectsAnimationsEntity.propertiesConfig(),
objectsAssets: ObjectsAssetsEntity.propertiesConfig(),
objects: ObjectsEntity.propertiesConfig(),
objectsItemsInventory: ObjectsItemsInventoryEntity.propertiesConfig(),
objectsItemsRequirements: ObjectsItemsRequirementsEntity.propertiesConfig(),
objectsItemsRewards: ObjectsItemsRewardsEntity.propertiesConfig(),
objectsSkills: ObjectsSkillsEntity.propertiesConfig(),
objectsStats: ObjectsStatsEntity.propertiesConfig(),
objectsTypes: ObjectsTypesEntity.propertiesConfig(),
operationTypes: OperationTypesEntity.propertiesConfig(),
players: PlayersEntity.propertiesConfig(),
playersState: PlayersStateEntity.propertiesConfig(),
playersStats: PlayersStatsEntity.propertiesConfig(),
respawn: RespawnEntity.propertiesConfig(),
rewards: RewardsEntity.propertiesConfig(),
rewardsEvents: RewardsEventsEntity.propertiesConfig(),
rewardsEventsState: RewardsEventsStateEntity.propertiesConfig(),
rewardsModifiers: RewardsModifiersEntity.propertiesConfig(),
roomsChangePoints: RoomsChangePointsEntity.propertiesConfig(),
rooms: RoomsEntity.propertiesConfig(),
roomsReturnPoints: RoomsReturnPointsEntity.propertiesConfig(),
scoresDetail: ScoresDetailEntity.propertiesConfig(),
scores: ScoresEntity.propertiesConfig(),
skillsClassLevelUpAnimations: SkillsClassLevelUpAnimationsEntity.propertiesConfig(),
skillsClassPath: SkillsClassPathEntity.propertiesConfig(),
skillsClassPathLevelLabels: SkillsClassPathLevelLabelsEntity.propertiesConfig(),
skillsClassPathLevelSkills: SkillsClassPathLevelSkillsEntity.propertiesConfig(),
skillsGroups: SkillsGroupsEntity.propertiesConfig(),
skillsLevels: SkillsLevelsEntity.propertiesConfig(),
skillsLevelsModifiersConditions: SkillsLevelsModifiersConditionsEntity.propertiesConfig(),
skillsLevelsModifiers: SkillsLevelsModifiersEntity.propertiesConfig(),
skillsLevelsSet: SkillsLevelsSetEntity.propertiesConfig(),
skillsOwnersClassPath: SkillsOwnersClassPathEntity.propertiesConfig(),
skillsSkillAnimations: SkillsSkillAnimationsEntity.propertiesConfig(),
skillsSkillAttack: SkillsSkillAttackEntity.propertiesConfig(),
skillsSkill: SkillsSkillEntity.propertiesConfig(),
skillsSkillGroupRelation: SkillsSkillGroupRelationEntity.propertiesConfig(),
skillsSkillOwnerConditions: SkillsSkillOwnerConditionsEntity.propertiesConfig(),
skillsSkillOwnerEffectsConditions: SkillsSkillOwnerEffectsConditionsEntity.propertiesConfig(),
skillsSkillOwnerEffects: SkillsSkillOwnerEffectsEntity.propertiesConfig(),
skillsSkillPhysicalData: SkillsSkillPhysicalDataEntity.propertiesConfig(),
skillsSkillTargetEffectsConditions: SkillsSkillTargetEffectsConditionsEntity.propertiesConfig(),
skillsSkillTargetEffects: SkillsSkillTargetEffectsEntity.propertiesConfig(),
skillsSkillType: SkillsSkillTypeEntity.propertiesConfig(),
snippets: SnippetsEntity.propertiesConfig(),
stats: StatsEntity.propertiesConfig(),
targetOptions: TargetOptionsEntity.propertiesConfig(),
users: UsersEntity.propertiesConfig(),
usersLocale: UsersLocaleEntity.propertiesConfig(),
usersLogin: UsersLoginEntity.propertiesConfig()
};
module.exports.entitiesConfig = entitiesConfig;
================================================
FILE: generated-entities/entities-translations.js
================================================
/**
*
* Reldens - Entities Translations
*
*/
module.exports.entitiesTranslations = {
labels: {
'ads_banner': 'Ads Banner',
'ads': 'Ads',
'ads_event_video': 'Ads Event Video',
'ads_played': 'Ads Played',
'ads_providers': 'Ads Providers',
'ads_types': 'Ads Types',
'audio_categories': 'Audio Categories',
'audio': 'Audio',
'audio_markers': 'Audio Markers',
'audio_player_config': 'Audio Player Config',
'chat': 'Chat',
'chat_message_types': 'Chat Message Types',
'clan': 'Clan',
'clan_levels': 'Clan Levels',
'clan_levels_modifiers': 'Clan Levels Modifiers',
'clan_members': 'Clan Members',
'config': 'Config',
'config_types': 'Config Types',
'drops_animations': 'Drops Animations',
'features': 'Features',
'items_group': 'Items Group',
'items_inventory': 'Items Inventory',
'items_item': 'Items Item',
'items_item_modifiers': 'Items Item Modifiers',
'items_types': 'Items Types',
'locale': 'Locale',
'objects_animations': 'Objects Animations',
'objects_assets': 'Objects Assets',
'objects': 'Objects',
'objects_items_inventory': 'Objects Items Inventory',
'objects_items_requirements': 'Objects Items Requirements',
'objects_items_rewards': 'Objects Items Rewards',
'objects_skills': 'Objects Skills',
'objects_stats': 'Objects Stats',
'objects_types': 'Objects Types',
'operation_types': 'Operation Types',
'players': 'Players',
'players_state': 'Players State',
'players_stats': 'Players Stats',
'respawn': 'Respawn',
'rewards': 'Rewards',
'rewards_events': 'Rewards Events',
'rewards_events_state': 'Rewards Events State',
'rewards_modifiers': 'Rewards Modifiers',
'rooms_change_points': 'Rooms Change Points',
'rooms': 'Rooms',
'rooms_return_points': 'Rooms Return Points',
'scores_detail': 'Scores Detail',
'scores': 'Scores',
'skills_class_level_up_animations': 'Skills Class Level Up Animations',
'skills_class_path': 'Skills Class Path',
'skills_class_path_level_labels': 'Skills Class Path Level Labels',
'skills_class_path_level_skills': 'Skills Class Path Level Skills',
'skills_groups': 'Skills Groups',
'skills_levels': 'Skills Levels',
'skills_levels_modifiers_conditions': 'Skills Levels Modifiers Conditions',
'skills_levels_modifiers': 'Skills Levels Modifiers',
'skills_levels_set': 'Skills Levels Set',
'skills_owners_class_path': 'Skills Owners Class Path',
'skills_skill_animations': 'Skills Skill Animations',
'skills_skill_attack': 'Skills Skill Attack',
'skills_skill': 'Skills Skill',
'skills_skill_group_relation': 'Skills Skill Group Relation',
'skills_skill_owner_conditions': 'Skills Skill Owner Conditions',
'skills_skill_owner_effects_conditions': 'Skills Skill Owner Effects Conditions',
'skills_skill_owner_effects': 'Skills Skill Owner Effects',
'skills_skill_physical_data': 'Skills Skill Physical Data',
'skills_skill_target_effects_conditions': 'Skills Skill Target Effects Conditions',
'skills_skill_target_effects': 'Skills Skill Target Effects',
'skills_skill_type': 'Skills Skill Type',
'snippets': 'Snippets',
'stats': 'Stats',
'target_options': 'Target Options',
'users': 'Users',
'users_locale': 'Users Locale',
'users_login': 'Users Login'
},
fields: {
'ads_banner': {
'id': 'ID',
'ads_id': 'Ads ID',
'banner_data': 'Banner Data'
},
'ads': {
'id': 'ID',
'key': 'Key',
'provider_id': 'Provider ID',
'type_id': 'Type ID',
'width': 'Width',
'height': 'Height',
'position': 'Position',
'top': 'Top',
'bottom': 'Bottom',
'left': 'Left',
'right': 'Right',
'replay': 'Replay',
'enabled': 'Enabled',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'ads_event_video': {
'id': 'ID',
'ads_id': 'Ads ID',
'event_key': 'Event Key',
'event_data': 'Event Data'
},
'ads_played': {
'id': 'ID',
'ads_id': 'Ads ID',
'player_id': 'Player ID',
'started_at': 'Started At',
'ended_at': 'Ended At'
},
'ads_providers': {
'id': 'ID',
'key': 'Key',
'enabled': 'Enabled'
},
'ads_types': {
'id': 'ID',
'key': 'Key'
},
'audio_categories': {
'id': 'ID',
'category_key': 'Category Key',
'category_label': 'Category Label',
'enabled': 'Enabled',
'single_audio': 'Single Audio',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'audio': {
'id': 'ID',
'audio_key': 'Audio Key',
'files_name': 'Files Name',
'config': 'Config',
'room_id': 'Room ID',
'category_id': 'Category ID',
'enabled': 'Enabled',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'audio_markers': {
'id': 'ID',
'audio_id': 'Audio ID',
'marker_key': 'Marker Key',
'start': 'Start',
'duration': 'Duration',
'config': 'Config'
},
'audio_player_config': {
'id': 'ID',
'player_id': 'Player ID',
'category_id': 'Category ID',
'enabled': 'Enabled'
},
'chat': {
'id': 'ID',
'player_id': 'Player ID',
'room_id': 'Room ID',
'message': 'Message',
'private_player_id': 'Private Player ID',
'message_type': 'Message Type',
'message_time': 'Message Time'
},
'chat_message_types': {
'id': 'ID',
'key': 'Key',
'show_tab': 'Show Tab',
'also_show_in_type': 'Also Show In Type'
},
'clan': {
'id': 'ID',
'owner_id': 'Owner ID',
'name': 'Name',
'points': 'Points',
'level': 'Level',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'clan_levels': {
'id': 'ID',
'key': 'Key',
'label': 'Label',
'required_experience': 'Required Experience'
},
'clan_levels_modifiers': {
'id': 'ID',
'level_id': 'Level ID',
'key': 'Key',
'property_key': 'Property Key',
'operation': 'Operation',
'value': 'Value',
'minValue': 'MinValue',
'maxValue': 'MaxValue',
'minProperty': 'MinProperty',
'maxProperty': 'MaxProperty'
},
'clan_members': {
'id': 'ID',
'clan_id': 'Clan ID',
'player_id': 'Player ID'
},
'config': {
'id': 'ID',
'scope': 'Scope',
'path': 'Path',
'value': 'Value',
'type': 'Type'
},
'config_types': {
'id': 'ID',
'label': 'Label'
},
'drops_animations': {
'id': 'ID',
'item_id': 'Item ID',
'asset_type': 'Asset Type',
'asset_key': 'Asset Key',
'file': 'File',
'extra_params': 'Extra Params'
},
'features': {
'id': 'ID',
'code': 'Code',
'title': 'Title',
'is_enabled': 'Is Enabled'
},
'items_group': {
'id': 'ID',
'key': 'Key',
'label': 'Label',
'description': 'Description',
'files_name': 'Files Name',
'sort': 'Sort',
'items_limit': 'Items Limit',
'limit_per_item': 'Limit Per Item'
},
'items_inventory': {
'id': 'ID',
'owner_id': 'Owner ID',
'item_id': 'Item ID',
'qty': 'Qty',
'remaining_uses': 'Remaining Uses',
'is_active': 'Is Active'
},
'items_item': {
'id': 'ID',
'key': 'Key',
'type': 'Type',
'group_id': 'Group ID',
'label': 'Label',
'description': 'Description',
'qty_limit': 'Qty Limit',
'uses_limit': 'Uses Limit',
'useTimeOut': 'UseTimeOut',
'execTimeOut': 'ExecTimeOut',
'customData': 'CustomData',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'items_item_modifiers': {
'id': 'ID',
'item_id': 'Item ID',
'key': 'Key',
'property_key': 'Property Key',
'operation': 'Operation',
'value': 'Value',
'maxProperty': 'MaxProperty'
},
'items_types': {
'id': 'ID',
'key': 'Key'
},
'locale': {
'id': 'ID',
'locale': 'Locale',
'language_code': 'Language Code',
'country_code': 'Country Code',
'enabled': 'Enabled'
},
'objects_animations': {
'id': 'ID',
'object_id': 'Object ID',
'animationKey': 'AnimationKey',
'animationData': 'AnimationData'
},
'objects_assets': {
'object_asset_id': 'Object Asset ID',
'object_id': 'Object ID',
'asset_type': 'Asset Type',
'asset_key': 'Asset Key',
'asset_file': 'Asset File',
'extra_params': 'Extra Params'
},
'objects': {
'id': 'ID',
'room_id': 'Room ID',
'layer_name': 'Layer Name',
'tile_index': 'Tile Index',
'class_type': 'Class Type',
'object_class_key': 'Object Class Key',
'client_key': 'Client Key',
'title': 'Title',
'private_params': 'Private Params',
'client_params': 'Client Params',
'enabled': 'Enabled',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'objects_items_inventory': {
'id': 'ID',
'owner_id': 'Owner ID',
'item_id': 'Item ID',
'qty': 'Qty',
'remaining_uses': 'Remaining Uses',
'is_active': 'Is Active'
},
'objects_items_requirements': {
'id': 'ID',
'object_id': 'Object ID',
'item_key': 'Item Key',
'required_item_key': 'Required Item Key',
'required_quantity': 'Required Quantity',
'auto_remove_requirement': 'Auto Remove Requirement'
},
'objects_items_rewards': {
'id': 'ID',
'object_id': 'Object ID',
'item_key': 'Item Key',
'reward_item_key': 'Reward Item Key',
'reward_quantity': 'Reward Quantity',
'reward_item_is_required': 'Reward Item Is Required'
},
'objects_skills': {
'id': 'ID',
'object_id': 'Object ID',
'skill_id': 'Skill ID',
'target_id': 'Target ID'
},
'objects_stats': {
'id': 'ID',
'object_id': 'Object ID',
'stat_id': 'Stat ID',
'base_value': 'Base Value',
'value': 'Value'
},
'objects_types': {
'id': 'ID',
'key': 'Key'
},
'operation_types': {
'id': 'ID',
'label': 'Label',
'key': 'Key'
},
'players': {
'id': 'ID',
'user_id': 'User ID',
'name': 'Name',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'players_state': {
'id': 'ID',
'player_id': 'Player ID',
'room_id': 'Room ID',
'x': 'X',
'y': 'Y',
'dir': 'Dir'
},
'players_stats': {
'id': 'ID',
'player_id': 'Player ID',
'stat_id': 'Stat ID',
'base_value': 'Base Value',
'value': 'Value'
},
'respawn': {
'id': 'ID',
'object_id': 'Object ID',
'respawn_time': 'Respawn Time',
'instances_limit': 'Instances Limit',
'layer': 'Layer',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'rewards': {
'id': 'ID',
'object_id': 'Object ID',
'item_id': 'Item ID',
'modifier_id': 'Modifier ID',
'experience': 'Experience',
'drop_rate': 'Drop Rate',
'drop_quantity': 'Drop Quantity',
'is_unique': 'Is Unique',
'was_given': 'Was Given',
'has_drop_body': 'Has Drop Body',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'rewards_events': {
'id': 'ID',
'label': 'Label',
'description': 'Description',
'handler_key': 'Handler Key',
'event_key': 'Event Key',
'event_data': 'Event Data',
'position': 'Position',
'enabled': 'Enabled',
'active_from': 'Active From',
'active_to': 'Active To'
},
'rewards_events_state': {
'id': 'ID',
'rewards_events_id': 'Rewards Events ID',
'player_id': 'Player ID',
'state': 'State'
},
'rewards_modifiers': {
'id': 'ID',
'key': 'Key',
'property_key': 'Property Key',
'operation': 'Operation',
'value': 'Value',
'minValue': 'MinValue',
'maxValue': 'MaxValue',
'minProperty': 'MinProperty',
'maxProperty': 'MaxProperty'
},
'rooms_change_points': {
'id': 'ID',
'room_id': 'Room ID',
'tile_index': 'Tile Index',
'next_room_id': 'Next Room ID'
},
'rooms': {
'id': 'ID',
'name': 'Name',
'title': 'Title',
'map_filename': 'Map Filename',
'scene_images': 'Scene Images',
'room_class_key': 'Room Class Key',
'server_url': 'Server Url',
'customData': 'CustomData',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'rooms_return_points': {
'id': 'ID',
'room_id': 'Room ID',
'direction': 'Direction',
'x': 'X',
'y': 'Y',
'is_default': 'Is Default',
'from_room_id': 'From Room ID'
},
'scores_detail': {
'id': 'ID',
'player_id': 'Player ID',
'obtained_score': 'Obtained Score',
'kill_time': 'Kill Time',
'kill_player_id': 'Kill Player ID',
'kill_npc_id': 'Kill Npc ID'
},
'scores': {
'id': 'ID',
'player_id': 'Player ID',
'total_score': 'Total Score',
'players_kills_count': 'Players Kills Count',
'npcs_kills_count': 'Npcs Kills Count',
'last_player_kill_time': 'Last Player Kill Time',
'last_npc_kill_time': 'Last Npc Kill Time',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'skills_class_level_up_animations': {
'id': 'ID',
'class_path_id': 'Class Path ID',
'level_id': 'Level ID',
'animationData': 'AnimationData'
},
'skills_class_path': {
'id': 'ID',
'key': 'Key',
'label': 'Label',
'levels_set_id': 'Levels Set ID',
'enabled': 'Enabled',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'skills_class_path_level_labels': {
'id': 'ID',
'class_path_id': 'Class Path ID',
'level_id': 'Level ID',
'label': 'Label'
},
'skills_class_path_level_skills': {
'id': 'ID',
'class_path_id': 'Class Path ID',
'level_id': 'Level ID',
'skill_id': 'Skill ID'
},
'skills_groups': {
'id': 'ID',
'key': 'Key',
'label': 'Label',
'description': 'Description',
'sort': 'Sort'
},
'skills_levels': {
'id': 'ID',
'key': 'Key',
'label': 'Label',
'required_experience': 'Required Experience',
'level_set_id': 'Level Set ID'
},
'skills_levels_modifiers_conditions': {
'id': 'ID',
'levels_modifier_id': 'Levels Modifier ID',
'key': 'Key',
'property_key': 'Property Key',
'conditional': 'Conditional',
'value': 'Value'
},
'skills_levels_modifiers': {
'id': 'ID',
'level_id': 'Level ID',
'key': 'Key',
'property_key': 'Property Key',
'operation': 'Operation',
'value': 'Value',
'minValue': 'MinValue',
'maxValue': 'MaxValue',
'minProperty': 'MinProperty',
'maxProperty': 'MaxProperty'
},
'skills_levels_set': {
'id': 'ID',
'key': 'Key',
'label': 'Label',
'autoFillRanges': 'AutoFillRanges',
'autoFillExperienceMultiplier': 'AutoFillExperienceMultiplier',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'skills_owners_class_path': {
'id': 'ID',
'class_path_id': 'Class Path ID',
'owner_id': 'Owner ID',
'currentLevel': 'CurrentLevel',
'currentExp': 'CurrentExp'
},
'skills_skill_animations': {
'id': 'ID',
'skill_id': 'Skill ID',
'key': 'Key',
'classKey': 'ClassKey',
'animationData': 'AnimationData'
},
'skills_skill_attack': {
'id': 'ID',
'skill_id': 'Skill ID',
'affectedProperty': 'AffectedProperty',
'allowEffectBelowZero': 'AllowEffectBelowZero',
'hitDamage': 'HitDamage',
'applyDirectDamage': 'ApplyDirectDamage',
'attackProperties': 'AttackProperties',
'defenseProperties': 'DefenseProperties',
'aimProperties': 'AimProperties',
'dodgeProperties': 'DodgeProperties',
'dodgeFullEnabled': 'DodgeFullEnabled',
'dodgeOverAimSuccess': 'DodgeOverAimSuccess',
'damageAffected': 'DamageAffected',
'criticalAffected': 'CriticalAffected'
},
'skills_skill': {
'id': 'ID',
'key': 'Key',
'type': 'Type',
'label': 'Label',
'autoValidation': 'AutoValidation',
'skillDelay': 'SkillDelay',
'castTime': 'CastTime',
'usesLimit': 'UsesLimit',
'range': 'Range',
'rangeAutomaticValidation': 'RangeAutomaticValidation',
'rangePropertyX': 'RangePropertyX',
'rangePropertyY': 'RangePropertyY',
'rangeTargetPropertyX': 'RangeTargetPropertyX',
'rangeTargetPropertyY': 'RangeTargetPropertyY',
'allowSelfTarget': 'AllowSelfTarget',
'criticalChance': 'CriticalChance',
'criticalMultiplier': 'CriticalMultiplier',
'criticalFixedValue': 'CriticalFixedValue',
'customData': 'CustomData',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'skills_skill_group_relation': {
'id': 'ID',
'skill_id': 'Skill ID',
'group_id': 'Group ID'
},
'skills_skill_owner_conditions': {
'id': 'ID',
'skill_id': 'Skill ID',
'key': 'Key',
'property_key': 'Property Key',
'conditional': 'Conditional',
'value': 'Value'
},
'skills_skill_owner_effects_conditions': {
'id': 'ID',
'skill_owner_effect_id': 'Skill Owner Effect ID',
'key': 'Key',
'property_key': 'Property Key',
'conditional': 'Conditional',
'value': 'Value'
},
'skills_skill_owner_effects': {
'id': 'ID',
'skill_id': 'Skill ID',
'key': 'Key',
'property_key': 'Property Key',
'operation': 'Operation',
'value': 'Value',
'minValue': 'MinValue',
'maxValue': 'MaxValue',
'minProperty': 'MinProperty',
'maxProperty': 'MaxProperty'
},
'skills_skill_physical_data': {
'id': 'ID',
'skill_id': 'Skill ID',
'magnitude': 'Magnitude',
'objectWidth': 'ObjectWidth',
'objectHeight': 'ObjectHeight',
'validateTargetOnHit': 'ValidateTargetOnHit'
},
'skills_skill_target_effects_conditions': {
'id': 'ID',
'skill_target_effect_id': 'Skill Target Effect ID',
'key': 'Key',
'property_key': 'Property Key',
'conditional': 'Conditional',
'value': 'Value'
},
'skills_skill_target_effects': {
'id': 'ID',
'skill_id': 'Skill ID',
'key': 'Key',
'property_key': 'Property Key',
'operation': 'Operation',
'value': 'Value',
'minValue': 'MinValue',
'maxValue': 'MaxValue',
'minProperty': 'MinProperty',
'maxProperty': 'MaxProperty'
},
'skills_skill_type': {
'id': 'ID',
'key': 'Key'
},
'snippets': {
'id': 'ID',
'locale_id': 'Locale ID',
'key': 'Key',
'value': 'Value',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'stats': {
'id': 'ID',
'key': 'Key',
'label': 'Label',
'description': 'Description',
'base_value': 'Base Value',
'customData': 'CustomData',
'created_at': 'Created At',
'updated_at': 'Updated At'
},
'target_options': {
'id': 'ID',
'target_key': 'Target Key',
'target_label': 'Target Label'
},
'users': {
'id': 'ID',
'email': 'Email',
'username': 'Username',
'password': 'Password',
'role_id': 'Role ID',
'status': 'Status',
'created_at': 'Created At',
'updated_at': 'Updated At',
'played_time': 'Played Time',
'login_count': 'Login Count'
},
'users_locale': {
'id': 'ID',
'locale_id': 'Locale ID',
'user_id': 'User ID'
},
'users_login': {
'id': 'ID',
'user_id': 'User ID',
'login_date': 'Login Date',
'logout_date': 'Logout Date'
}
}
};
================================================
FILE: generated-entities/models/mikro-orm/ads-banner-model.js
================================================
/**
*
* Reldens - AdsBannerModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class AdsBannerModel
{
constructor(id, ads_id, banner_data)
{
this.id = id;
this.ads_id = ads_id;
this.banner_data = banner_data;
}
static createByProps(props)
{
const {id, ads_id, banner_data} = props;
return new this(id, ads_id, banner_data);
}
}
const schema = new EntitySchema({
class: AdsBannerModel,
tableName: 'ads_banner',
properties: {
id: { type: 'number', primary: true },
ads_id: { type: 'number', persist: false },
banner_data: { type: 'string' },
related_ads: {
kind: 'm:1',
entity: 'AdsModel',
joinColumn: 'ads_id'
}
},
});
schema._fkMappings = {
"ads_id": {
"relationKey": "related_ads",
"entityName": "AdsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
AdsBannerModel,
entity: AdsBannerModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/ads-event-video-model.js
================================================
/**
*
* Reldens - AdsEventVideoModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class AdsEventVideoModel
{
constructor(id, ads_id, event_key, event_data)
{
this.id = id;
this.ads_id = ads_id;
this.event_key = event_key;
this.event_data = event_data;
}
static createByProps(props)
{
const {id, ads_id, event_key, event_data} = props;
return new this(id, ads_id, event_key, event_data);
}
}
const schema = new EntitySchema({
class: AdsEventVideoModel,
tableName: 'ads_event_video',
properties: {
id: { type: 'number', primary: true },
ads_id: { type: 'number', persist: false },
event_key: { type: 'string' },
event_data: { type: 'string' },
related_ads: {
kind: 'm:1',
entity: 'AdsModel',
joinColumn: 'ads_id'
}
},
});
schema._fkMappings = {
"ads_id": {
"relationKey": "related_ads",
"entityName": "AdsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
AdsEventVideoModel,
entity: AdsEventVideoModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/ads-model.js
================================================
/**
*
* Reldens - AdsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class AdsModel
{
constructor(id, key, provider_id, type_id, width, height, position, top, bottom, left, right, replay, enabled, created_at, updated_at)
{
this.id = id;
this.key = key;
this.provider_id = provider_id;
this.type_id = type_id;
this.width = width;
this.height = height;
this.position = position;
this.top = top;
this.bottom = bottom;
this.left = left;
this.right = right;
this.replay = replay;
this.enabled = enabled;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, key, provider_id, type_id, width, height, position, top, bottom, left, right, replay, enabled, created_at, updated_at} = props;
return new this(id, key, provider_id, type_id, width, height, position, top, bottom, left, right, replay, enabled, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: AdsModel,
tableName: 'ads',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
provider_id: { type: 'number', persist: false },
type_id: { type: 'number', persist: false },
width: { type: 'number', nullable: true },
height: { type: 'number', nullable: true },
position: { type: 'string', nullable: true },
top: { type: 'number', nullable: true },
bottom: { type: 'number', nullable: true },
left: { type: 'number', nullable: true },
right: { type: 'number', nullable: true },
replay: { type: 'number', nullable: true },
enabled: { type: 'number', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_ads_providers: {
kind: 'm:1',
entity: 'AdsProvidersModel',
joinColumn: 'provider_id'
},
related_ads_types: {
kind: 'm:1',
entity: 'AdsTypesModel',
joinColumn: 'type_id'
},
related_ads_banner: {
kind: '1:1',
entity: 'AdsBannerModel',
mappedBy: 'related_ads'
},
related_ads_event_video: {
kind: '1:1',
entity: 'AdsEventVideoModel',
mappedBy: 'related_ads'
},
related_ads_played: {
kind: '1:m',
entity: 'AdsPlayedModel',
mappedBy: 'related_ads'
}
},
});
schema._fkMappings = {
"provider_id": {
"relationKey": "related_ads_providers",
"entityName": "AdsProvidersModel",
"referencedColumn": "id",
"nullable": false
},
"type_id": {
"relationKey": "related_ads_types",
"entityName": "AdsTypesModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
AdsModel,
entity: AdsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/ads-played-model.js
================================================
/**
*
* Reldens - AdsPlayedModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class AdsPlayedModel
{
constructor(id, ads_id, player_id, started_at, ended_at)
{
this.id = id;
this.ads_id = ads_id;
this.player_id = player_id;
this.started_at = started_at;
this.ended_at = ended_at;
}
static createByProps(props)
{
const {id, ads_id, player_id, started_at, ended_at} = props;
return new this(id, ads_id, player_id, started_at, ended_at);
}
}
const schema = new EntitySchema({
class: AdsPlayedModel,
tableName: 'ads_played',
properties: {
id: { type: 'number', primary: true },
ads_id: { type: 'number', persist: false },
player_id: { type: 'number', persist: false },
started_at: { type: 'Date', nullable: true },
ended_at: { type: 'Date', nullable: true },
related_ads: {
kind: 'm:1',
entity: 'AdsModel',
joinColumn: 'ads_id'
},
related_players: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'player_id'
}
},
});
schema._fkMappings = {
"ads_id": {
"relationKey": "related_ads",
"entityName": "AdsModel",
"referencedColumn": "id",
"nullable": false
},
"player_id": {
"relationKey": "related_players",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
AdsPlayedModel,
entity: AdsPlayedModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/ads-providers-model.js
================================================
/**
*
* Reldens - AdsProvidersModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class AdsProvidersModel
{
constructor(id, key, enabled)
{
this.id = id;
this.key = key;
this.enabled = enabled;
}
static createByProps(props)
{
const {id, key, enabled} = props;
return new this(id, key, enabled);
}
}
const schema = new EntitySchema({
class: AdsProvidersModel,
tableName: 'ads_providers',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
enabled: { type: 'number', nullable: true },
related_ads: {
kind: '1:m',
entity: 'AdsModel',
mappedBy: 'related_ads_providers'
}
},
});
module.exports = {
AdsProvidersModel,
entity: AdsProvidersModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/ads-types-model.js
================================================
/**
*
* Reldens - AdsTypesModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class AdsTypesModel
{
constructor(id, key)
{
this.id = id;
this.key = key;
}
static createByProps(props)
{
const {id, key} = props;
return new this(id, key);
}
}
const schema = new EntitySchema({
class: AdsTypesModel,
tableName: 'ads_types',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
related_ads: {
kind: '1:m',
entity: 'AdsModel',
mappedBy: 'related_ads_types'
}
},
});
module.exports = {
AdsTypesModel,
entity: AdsTypesModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/audio-categories-model.js
================================================
/**
*
* Reldens - AudioCategoriesModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class AudioCategoriesModel
{
constructor(id, category_key, category_label, enabled, single_audio, created_at, updated_at)
{
this.id = id;
this.category_key = category_key;
this.category_label = category_label;
this.enabled = enabled;
this.single_audio = single_audio;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, category_key, category_label, enabled, single_audio, created_at, updated_at} = props;
return new this(id, category_key, category_label, enabled, single_audio, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: AudioCategoriesModel,
tableName: 'audio_categories',
properties: {
id: { type: 'number', primary: true },
category_key: { type: 'string' },
category_label: { type: 'string' },
enabled: { type: 'number', nullable: true },
single_audio: { type: 'number', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_audio: {
kind: '1:m',
entity: 'AudioModel',
mappedBy: 'related_audio_categories'
},
related_audio_player_config: {
kind: '1:m',
entity: 'AudioPlayerConfigModel',
mappedBy: 'related_audio_categories'
}
},
});
module.exports = {
AudioCategoriesModel,
entity: AudioCategoriesModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/audio-markers-model.js
================================================
/**
*
* Reldens - AudioMarkersModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class AudioMarkersModel
{
constructor(id, audio_id, marker_key, start, duration, config)
{
this.id = id;
this.audio_id = audio_id;
this.marker_key = marker_key;
this.start = start;
this.duration = duration;
this.config = config;
}
static createByProps(props)
{
const {id, audio_id, marker_key, start, duration, config} = props;
return new this(id, audio_id, marker_key, start, duration, config);
}
}
const schema = new EntitySchema({
class: AudioMarkersModel,
tableName: 'audio_markers',
properties: {
id: { type: 'number', primary: true },
audio_id: { type: 'number', persist: false },
marker_key: { type: 'string' },
start: { type: 'number' },
duration: { type: 'number' },
config: { type: 'string', nullable: true },
related_audio: {
kind: 'm:1',
entity: 'AudioModel',
joinColumn: 'audio_id'
}
},
});
schema._fkMappings = {
"audio_id": {
"relationKey": "related_audio",
"entityName": "AudioModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
AudioMarkersModel,
entity: AudioMarkersModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/audio-model.js
================================================
/**
*
* Reldens - AudioModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class AudioModel
{
constructor(id, audio_key, files_name, config, room_id, category_id, enabled, created_at, updated_at)
{
this.id = id;
this.audio_key = audio_key;
this.files_name = files_name;
this.config = config;
this.room_id = room_id;
this.category_id = category_id;
this.enabled = enabled;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, audio_key, files_name, config, room_id, category_id, enabled, created_at, updated_at} = props;
return new this(id, audio_key, files_name, config, room_id, category_id, enabled, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: AudioModel,
tableName: 'audio',
properties: {
id: { type: 'number', primary: true },
audio_key: { type: 'string' },
files_name: { type: 'string' },
config: { type: 'string', nullable: true },
room_id: { type: 'number', persist: false },
category_id: { type: 'number', persist: false },
enabled: { type: 'number', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_rooms: {
kind: 'm:1',
entity: 'RoomsModel',
joinColumn: 'room_id'
},
related_audio_categories: {
kind: 'm:1',
entity: 'AudioCategoriesModel',
joinColumn: 'category_id'
},
related_audio_markers: {
kind: '1:m',
entity: 'AudioMarkersModel',
mappedBy: 'related_audio'
}
},
});
schema._fkMappings = {
"room_id": {
"relationKey": "related_rooms",
"entityName": "RoomsModel",
"referencedColumn": "id",
"nullable": true
},
"category_id": {
"relationKey": "related_audio_categories",
"entityName": "AudioCategoriesModel",
"referencedColumn": "id",
"nullable": true
}
};
module.exports = {
AudioModel,
entity: AudioModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/audio-player-config-model.js
================================================
/**
*
* Reldens - AudioPlayerConfigModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class AudioPlayerConfigModel
{
constructor(id, player_id, category_id, enabled)
{
this.id = id;
this.player_id = player_id;
this.category_id = category_id;
this.enabled = enabled;
}
static createByProps(props)
{
const {id, player_id, category_id, enabled} = props;
return new this(id, player_id, category_id, enabled);
}
}
const schema = new EntitySchema({
class: AudioPlayerConfigModel,
tableName: 'audio_player_config',
properties: {
id: { type: 'number', primary: true },
player_id: { type: 'number', persist: false },
category_id: { type: 'number', persist: false },
enabled: { type: 'number', nullable: true },
related_players: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'player_id'
},
related_audio_categories: {
kind: 'm:1',
entity: 'AudioCategoriesModel',
joinColumn: 'category_id'
}
},
});
schema._fkMappings = {
"player_id": {
"relationKey": "related_players",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
},
"category_id": {
"relationKey": "related_audio_categories",
"entityName": "AudioCategoriesModel",
"referencedColumn": "id",
"nullable": true
}
};
module.exports = {
AudioPlayerConfigModel,
entity: AudioPlayerConfigModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/chat-message-types-model.js
================================================
/**
*
* Reldens - ChatMessageTypesModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ChatMessageTypesModel
{
constructor(id, key, show_tab, also_show_in_type)
{
this.id = id;
this.key = key;
this.show_tab = show_tab;
this.also_show_in_type = also_show_in_type;
}
static createByProps(props)
{
const {id, key, show_tab, also_show_in_type} = props;
return new this(id, key, show_tab, also_show_in_type);
}
}
const schema = new EntitySchema({
class: ChatMessageTypesModel,
tableName: 'chat_message_types',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
show_tab: { type: 'number', nullable: true },
also_show_in_type: { type: 'number', persist: false },
related_chat_message_types: {
kind: 'm:1',
entity: 'ChatMessageTypesModel',
joinColumn: 'also_show_in_type'
},
related_chat: {
kind: '1:m',
entity: 'ChatModel',
mappedBy: 'related_chat_message_types'
}
},
});
schema._fkMappings = {
"also_show_in_type": {
"relationKey": "related_chat_message_types",
"entityName": "ChatMessageTypesModel",
"referencedColumn": "id",
"nullable": true
}
};
module.exports = {
ChatMessageTypesModel,
entity: ChatMessageTypesModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/chat-model.js
================================================
/**
*
* Reldens - ChatModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ChatModel
{
constructor(id, player_id, room_id, message, private_player_id, message_type, message_time)
{
this.id = id;
this.player_id = player_id;
this.room_id = room_id;
this.message = message;
this.private_player_id = private_player_id;
this.message_type = message_type;
this.message_time = message_time;
}
static createByProps(props)
{
const {id, player_id, room_id, message, private_player_id, message_type, message_time} = props;
return new this(id, player_id, room_id, message, private_player_id, message_type, message_time);
}
}
const schema = new EntitySchema({
class: ChatModel,
tableName: 'chat',
properties: {
id: { type: 'number', primary: true },
player_id: { type: 'number', persist: false },
room_id: { type: 'number', persist: false },
message: { type: 'string' },
private_player_id: { type: 'number', persist: false },
message_type: { type: 'number', persist: false },
message_time: { type: 'Date' },
related_players_player: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'player_id'
},
related_rooms: {
kind: 'm:1',
entity: 'RoomsModel',
joinColumn: 'room_id'
},
related_players_private_player: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'private_player_id'
},
related_chat_message_types: {
kind: 'm:1',
entity: 'ChatMessageTypesModel',
joinColumn: 'message_type'
}
},
});
schema._fkMappings = {
"player_id": {
"relationKey": "related_players_player",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
},
"room_id": {
"relationKey": "related_rooms",
"entityName": "RoomsModel",
"referencedColumn": "id",
"nullable": true
},
"private_player_id": {
"relationKey": "related_players_private_player",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": true
},
"message_type": {
"relationKey": "related_chat_message_types",
"entityName": "ChatMessageTypesModel",
"referencedColumn": "id",
"nullable": true
}
};
module.exports = {
ChatModel,
entity: ChatModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/clan-levels-model.js
================================================
/**
*
* Reldens - ClanLevelsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ClanLevelsModel
{
constructor(id, key, label, required_experience)
{
this.id = id;
this.key = key;
this.label = label;
this.required_experience = required_experience;
}
static createByProps(props)
{
const {id, key, label, required_experience} = props;
return new this(id, key, label, required_experience);
}
}
const schema = new EntitySchema({
class: ClanLevelsModel,
tableName: 'clan_levels',
properties: {
id: { type: 'number', primary: true },
key: { type: 'number' },
label: { type: 'string' },
required_experience: { type: 'number', nullable: true },
related_clan: {
kind: '1:m',
entity: 'ClanModel',
mappedBy: 'related_clan_levels'
},
related_clan_levels_modifiers: {
kind: '1:m',
entity: 'ClanLevelsModifiersModel',
mappedBy: 'related_clan_levels'
}
},
});
module.exports = {
ClanLevelsModel,
entity: ClanLevelsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/clan-levels-modifiers-model.js
================================================
/**
*
* Reldens - ClanLevelsModifiersModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ClanLevelsModifiersModel
{
constructor(id, level_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty)
{
this.id = id;
this.level_id = level_id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.minProperty = minProperty;
this.maxProperty = maxProperty;
}
static createByProps(props)
{
const {id, level_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty} = props;
return new this(id, level_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty);
}
}
const schema = new EntitySchema({
class: ClanLevelsModifiersModel,
tableName: 'clan_levels_modifiers',
properties: {
id: { type: 'number', primary: true },
level_id: { type: 'number', persist: false },
key: { type: 'string' },
property_key: { type: 'string' },
operation: { type: 'number', persist: false },
value: { type: 'string' },
minValue: { type: 'string', nullable: true },
maxValue: { type: 'string', nullable: true },
minProperty: { type: 'string', nullable: true },
maxProperty: { type: 'string', nullable: true },
related_clan_levels: {
kind: 'm:1',
entity: 'ClanLevelsModel',
joinColumn: 'level_id'
},
related_operation_types: {
kind: 'm:1',
entity: 'OperationTypesModel',
joinColumn: 'operation'
}
},
});
schema._fkMappings = {
"level_id": {
"relationKey": "related_clan_levels",
"entityName": "ClanLevelsModel",
"referencedColumn": "id",
"nullable": false
},
"operation": {
"relationKey": "related_operation_types",
"entityName": "OperationTypesModel",
"referencedColumn": "key",
"nullable": false
}
};
module.exports = {
ClanLevelsModifiersModel,
entity: ClanLevelsModifiersModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/clan-members-model.js
================================================
/**
*
* Reldens - ClanMembersModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ClanMembersModel
{
constructor(id, clan_id, player_id)
{
this.id = id;
this.clan_id = clan_id;
this.player_id = player_id;
}
static createByProps(props)
{
const {id, clan_id, player_id} = props;
return new this(id, clan_id, player_id);
}
}
const schema = new EntitySchema({
class: ClanMembersModel,
tableName: 'clan_members',
properties: {
id: { type: 'number', primary: true },
clan_id: { type: 'number', persist: false },
player_id: { type: 'number', persist: false },
related_clan: {
kind: 'm:1',
entity: 'ClanModel',
joinColumn: 'clan_id'
},
related_players: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'player_id'
}
},
});
schema._fkMappings = {
"clan_id": {
"relationKey": "related_clan",
"entityName": "ClanModel",
"referencedColumn": "id",
"nullable": false
},
"player_id": {
"relationKey": "related_players",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
ClanMembersModel,
entity: ClanMembersModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/clan-model.js
================================================
/**
*
* Reldens - ClanModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ClanModel
{
constructor(id, owner_id, name, points, level, created_at, updated_at)
{
this.id = id;
this.owner_id = owner_id;
this.name = name;
this.points = points;
this.level = level;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, owner_id, name, points, level, created_at, updated_at} = props;
return new this(id, owner_id, name, points, level, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: ClanModel,
tableName: 'clan',
properties: {
id: { type: 'number', primary: true },
owner_id: { type: 'number', persist: false },
name: { type: 'string' },
points: { type: 'number', nullable: true },
level: { type: 'number', persist: false },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_players: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'owner_id'
},
related_clan_levels: {
kind: 'm:1',
entity: 'ClanLevelsModel',
joinColumn: 'level'
},
related_clan_members: {
kind: '1:m',
entity: 'ClanMembersModel',
mappedBy: 'related_clan'
}
},
});
schema._fkMappings = {
"owner_id": {
"relationKey": "related_players",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
},
"level": {
"relationKey": "related_clan_levels",
"entityName": "ClanLevelsModel",
"referencedColumn": "key",
"nullable": false
}
};
module.exports = {
ClanModel,
entity: ClanModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/config-model.js
================================================
/**
*
* Reldens - ConfigModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ConfigModel
{
constructor(id, scope, path, value, type)
{
this.id = id;
this.scope = scope;
this.path = path;
this.value = value;
this.type = type;
}
static createByProps(props)
{
const {id, scope, path, value, type} = props;
return new this(id, scope, path, value, type);
}
}
const schema = new EntitySchema({
class: ConfigModel,
tableName: 'config',
properties: {
id: { type: 'number', primary: true },
scope: { type: 'string' },
path: { type: 'string' },
value: { type: 'string' },
type: { type: 'number', persist: false },
related_config_types: {
kind: 'm:1',
entity: 'ConfigTypesModel',
joinColumn: 'type'
}
},
});
schema._fkMappings = {
"type": {
"relationKey": "related_config_types",
"entityName": "ConfigTypesModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
ConfigModel,
entity: ConfigModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/config-types-model.js
================================================
/**
*
* Reldens - ConfigTypesModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ConfigTypesModel
{
constructor(id, label)
{
this.id = id;
this.label = label;
}
static createByProps(props)
{
const {id, label} = props;
return new this(id, label);
}
}
const schema = new EntitySchema({
class: ConfigTypesModel,
tableName: 'config_types',
properties: {
id: { type: 'number', primary: true },
label: { type: 'string' },
related_config: {
kind: '1:m',
entity: 'ConfigModel',
mappedBy: 'related_config_types'
}
},
});
module.exports = {
ConfigTypesModel,
entity: ConfigTypesModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/drops-animations-model.js
================================================
/**
*
* Reldens - DropsAnimationsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class DropsAnimationsModel
{
constructor(id, item_id, asset_type, asset_key, file, extra_params)
{
this.id = id;
this.item_id = item_id;
this.asset_type = asset_type;
this.asset_key = asset_key;
this.file = file;
this.extra_params = extra_params;
}
static createByProps(props)
{
const {id, item_id, asset_type, asset_key, file, extra_params} = props;
return new this(id, item_id, asset_type, asset_key, file, extra_params);
}
}
const schema = new EntitySchema({
class: DropsAnimationsModel,
tableName: 'drops_animations',
properties: {
id: { type: 'number', primary: true },
item_id: { type: 'number', persist: false },
asset_type: { type: 'string', nullable: true },
asset_key: { type: 'string' },
file: { type: 'string' },
extra_params: { type: 'string', nullable: true },
related_items_item: {
kind: 'm:1',
entity: 'ItemsItemModel',
joinColumn: 'item_id'
}
},
});
schema._fkMappings = {
"item_id": {
"relationKey": "related_items_item",
"entityName": "ItemsItemModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
DropsAnimationsModel,
entity: DropsAnimationsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/features-model.js
================================================
/**
*
* Reldens - FeaturesModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class FeaturesModel
{
constructor(id, code, title, is_enabled)
{
this.id = id;
this.code = code;
this.title = title;
this.is_enabled = is_enabled;
}
static createByProps(props)
{
const {id, code, title, is_enabled} = props;
return new this(id, code, title, is_enabled);
}
}
const schema = new EntitySchema({
class: FeaturesModel,
tableName: 'features',
properties: {
id: { type: 'number', primary: true },
code: { type: 'string' },
title: { type: 'string' },
is_enabled: { type: 'number', nullable: true }
},
});
module.exports = {
FeaturesModel,
entity: FeaturesModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/items-group-model.js
================================================
/**
*
* Reldens - ItemsGroupModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ItemsGroupModel
{
constructor(id, key, label, description, files_name, sort, items_limit, limit_per_item)
{
this.id = id;
this.key = key;
this.label = label;
this.description = description;
this.files_name = files_name;
this.sort = sort;
this.items_limit = items_limit;
this.limit_per_item = limit_per_item;
}
static createByProps(props)
{
const {id, key, label, description, files_name, sort, items_limit, limit_per_item} = props;
return new this(id, key, label, description, files_name, sort, items_limit, limit_per_item);
}
}
const schema = new EntitySchema({
class: ItemsGroupModel,
tableName: 'items_group',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
label: { type: 'string' },
description: { type: 'string', nullable: true },
files_name: { type: 'string', nullable: true },
sort: { type: 'number', nullable: true },
items_limit: { type: 'number', nullable: true },
limit_per_item: { type: 'number', nullable: true },
related_items_item: {
kind: '1:m',
entity: 'ItemsItemModel',
mappedBy: 'related_items_group'
}
},
});
module.exports = {
ItemsGroupModel,
entity: ItemsGroupModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/items-inventory-model.js
================================================
/**
*
* Reldens - ItemsInventoryModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ItemsInventoryModel
{
constructor(id, owner_id, item_id, qty, remaining_uses, is_active)
{
this.id = id;
this.owner_id = owner_id;
this.item_id = item_id;
this.qty = qty;
this.remaining_uses = remaining_uses;
this.is_active = is_active;
}
static createByProps(props)
{
const {id, owner_id, item_id, qty, remaining_uses, is_active} = props;
return new this(id, owner_id, item_id, qty, remaining_uses, is_active);
}
}
const schema = new EntitySchema({
class: ItemsInventoryModel,
tableName: 'items_inventory',
properties: {
id: { type: 'number', primary: true },
owner_id: { type: 'number', persist: false },
item_id: { type: 'number', persist: false },
qty: { type: 'number', nullable: true },
remaining_uses: { type: 'number', nullable: true },
is_active: { type: 'number', nullable: true },
related_players: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'owner_id'
},
related_items_item: {
kind: 'm:1',
entity: 'ItemsItemModel',
joinColumn: 'item_id'
}
},
});
schema._fkMappings = {
"owner_id": {
"relationKey": "related_players",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
},
"item_id": {
"relationKey": "related_items_item",
"entityName": "ItemsItemModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
ItemsInventoryModel,
entity: ItemsInventoryModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/items-item-model.js
================================================
/**
*
* Reldens - ItemsItemModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ItemsItemModel
{
constructor(id, key, type, group_id, label, description, qty_limit, uses_limit, useTimeOut, execTimeOut, customData, created_at, updated_at)
{
this.id = id;
this.key = key;
this.type = type;
this.group_id = group_id;
this.label = label;
this.description = description;
this.qty_limit = qty_limit;
this.uses_limit = uses_limit;
this.useTimeOut = useTimeOut;
this.execTimeOut = execTimeOut;
this.customData = customData;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, key, type, group_id, label, description, qty_limit, uses_limit, useTimeOut, execTimeOut, customData, created_at, updated_at} = props;
return new this(id, key, type, group_id, label, description, qty_limit, uses_limit, useTimeOut, execTimeOut, customData, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: ItemsItemModel,
tableName: 'items_item',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
type: { type: 'number', persist: false },
group_id: { type: 'number', persist: false },
label: { type: 'string' },
description: { type: 'string', nullable: true },
qty_limit: { type: 'number', nullable: true },
uses_limit: { type: 'number', nullable: true },
useTimeOut: { type: 'number', nullable: true },
execTimeOut: { type: 'number', nullable: true },
customData: { type: 'string', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_items_types: {
kind: 'm:1',
entity: 'ItemsTypesModel',
joinColumn: 'type'
},
related_items_group: {
kind: 'm:1',
entity: 'ItemsGroupModel',
joinColumn: 'group_id'
},
related_drops_animations: {
kind: '1:1',
entity: 'DropsAnimationsModel',
mappedBy: 'related_items_item'
},
related_items_inventory: {
kind: '1:m',
entity: 'ItemsInventoryModel',
mappedBy: 'related_items_item'
},
related_items_item_modifiers: {
kind: '1:m',
entity: 'ItemsItemModifiersModel',
mappedBy: 'related_items_item'
},
related_objects_items_inventory: {
kind: '1:m',
entity: 'ObjectsItemsInventoryModel',
mappedBy: 'related_items_item'
},
related_objects_items_requirements_item_key: {
kind: '1:m',
entity: 'ObjectsItemsRequirementsModel',
mappedBy: 'related_items_item_item_key'
},
related_objects_items_requirements_required_item_key: {
kind: '1:m',
entity: 'ObjectsItemsRequirementsModel',
mappedBy: 'related_items_item_item_key'
},
related_objects_items_rewards_item_key: {
kind: '1:m',
entity: 'ObjectsItemsRewardsModel',
mappedBy: 'related_items_item_item_key'
},
related_objects_items_rewards_reward_item_key: {
kind: '1:m',
entity: 'ObjectsItemsRewardsModel',
mappedBy: 'related_items_item_item_key'
},
related_rewards: {
kind: '1:m',
entity: 'RewardsModel',
mappedBy: 'related_items_item'
}
},
});
schema._fkMappings = {
"type": {
"relationKey": "related_items_types",
"entityName": "ItemsTypesModel",
"referencedColumn": "id",
"nullable": false
},
"group_id": {
"relationKey": "related_items_group",
"entityName": "ItemsGroupModel",
"referencedColumn": "id",
"nullable": true
}
};
module.exports = {
ItemsItemModel,
entity: ItemsItemModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/items-item-modifiers-model.js
================================================
/**
*
* Reldens - ItemsItemModifiersModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ItemsItemModifiersModel
{
constructor(id, item_id, key, property_key, operation, value, maxProperty)
{
this.id = id;
this.item_id = item_id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.maxProperty = maxProperty;
}
static createByProps(props)
{
const {id, item_id, key, property_key, operation, value, maxProperty} = props;
return new this(id, item_id, key, property_key, operation, value, maxProperty);
}
}
const schema = new EntitySchema({
class: ItemsItemModifiersModel,
tableName: 'items_item_modifiers',
properties: {
id: { type: 'number', primary: true },
item_id: { type: 'number', persist: false },
key: { type: 'string' },
property_key: { type: 'string' },
operation: { type: 'number', persist: false },
value: { type: 'string' },
maxProperty: { type: 'string', nullable: true },
related_items_item: {
kind: 'm:1',
entity: 'ItemsItemModel',
joinColumn: 'item_id'
},
related_operation_types: {
kind: 'm:1',
entity: 'OperationTypesModel',
joinColumn: 'operation'
}
},
});
schema._fkMappings = {
"item_id": {
"relationKey": "related_items_item",
"entityName": "ItemsItemModel",
"referencedColumn": "id",
"nullable": false
},
"operation": {
"relationKey": "related_operation_types",
"entityName": "OperationTypesModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
ItemsItemModifiersModel,
entity: ItemsItemModifiersModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/items-types-model.js
================================================
/**
*
* Reldens - ItemsTypesModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ItemsTypesModel
{
constructor(id, key)
{
this.id = id;
this.key = key;
}
static createByProps(props)
{
const {id, key} = props;
return new this(id, key);
}
}
const schema = new EntitySchema({
class: ItemsTypesModel,
tableName: 'items_types',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
related_items_item: {
kind: '1:m',
entity: 'ItemsItemModel',
mappedBy: 'related_items_types'
}
},
});
module.exports = {
ItemsTypesModel,
entity: ItemsTypesModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/locale-model.js
================================================
/**
*
* Reldens - LocaleModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class LocaleModel
{
constructor(id, locale, language_code, country_code, enabled)
{
this.id = id;
this.locale = locale;
this.language_code = language_code;
this.country_code = country_code;
this.enabled = enabled;
}
static createByProps(props)
{
const {id, locale, language_code, country_code, enabled} = props;
return new this(id, locale, language_code, country_code, enabled);
}
}
const schema = new EntitySchema({
class: LocaleModel,
tableName: 'locale',
properties: {
id: { type: 'number', primary: true },
locale: { type: 'string' },
language_code: { type: 'string' },
country_code: { type: 'string', nullable: true },
enabled: { type: 'number', nullable: true },
related_snippets: {
kind: '1:m',
entity: 'SnippetsModel',
mappedBy: 'related_locale'
},
related_users_locale: {
kind: '1:m',
entity: 'UsersLocaleModel',
mappedBy: 'related_locale'
}
},
});
module.exports = {
LocaleModel,
entity: LocaleModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/objects-animations-model.js
================================================
/**
*
* Reldens - ObjectsAnimationsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ObjectsAnimationsModel
{
constructor(id, object_id, animationKey, animationData)
{
this.id = id;
this.object_id = object_id;
this.animationKey = animationKey;
this.animationData = animationData;
}
static createByProps(props)
{
const {id, object_id, animationKey, animationData} = props;
return new this(id, object_id, animationKey, animationData);
}
}
const schema = new EntitySchema({
class: ObjectsAnimationsModel,
tableName: 'objects_animations',
properties: {
id: { type: 'number', primary: true },
object_id: { type: 'number', persist: false },
animationKey: { type: 'string' },
animationData: { type: 'string' },
related_objects: {
kind: 'm:1',
entity: 'ObjectsModel',
joinColumn: 'object_id'
}
},
});
schema._fkMappings = {
"object_id": {
"relationKey": "related_objects",
"entityName": "ObjectsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
ObjectsAnimationsModel,
entity: ObjectsAnimationsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/objects-assets-model.js
================================================
/**
*
* Reldens - ObjectsAssetsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ObjectsAssetsModel
{
constructor(object_asset_id, object_id, asset_type, asset_key, asset_file, extra_params)
{
this.object_asset_id = object_asset_id;
this.object_id = object_id;
this.asset_type = asset_type;
this.asset_key = asset_key;
this.asset_file = asset_file;
this.extra_params = extra_params;
}
static createByProps(props)
{
const {object_asset_id, object_id, asset_type, asset_key, asset_file, extra_params} = props;
return new this(object_asset_id, object_id, asset_type, asset_key, asset_file, extra_params);
}
}
const schema = new EntitySchema({
class: ObjectsAssetsModel,
tableName: 'objects_assets',
properties: {
object_asset_id: { type: 'number', primary: true },
object_id: { type: 'number', persist: false },
asset_type: { type: 'string' },
asset_key: { type: 'string' },
asset_file: { type: 'string' },
extra_params: { type: 'string', nullable: true },
related_objects: {
kind: 'm:1',
entity: 'ObjectsModel',
joinColumn: 'object_id'
}
},
});
schema._fkMappings = {
"object_id": {
"relationKey": "related_objects",
"entityName": "ObjectsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
ObjectsAssetsModel,
entity: ObjectsAssetsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/objects-items-inventory-model.js
================================================
/**
*
* Reldens - ObjectsItemsInventoryModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ObjectsItemsInventoryModel
{
constructor(id, owner_id, item_id, qty, remaining_uses, is_active)
{
this.id = id;
this.owner_id = owner_id;
this.item_id = item_id;
this.qty = qty;
this.remaining_uses = remaining_uses;
this.is_active = is_active;
}
static createByProps(props)
{
const {id, owner_id, item_id, qty, remaining_uses, is_active} = props;
return new this(id, owner_id, item_id, qty, remaining_uses, is_active);
}
}
const schema = new EntitySchema({
class: ObjectsItemsInventoryModel,
tableName: 'objects_items_inventory',
properties: {
id: { type: 'number', primary: true },
owner_id: { type: 'number', persist: false },
item_id: { type: 'number', persist: false },
qty: { type: 'number', nullable: true },
remaining_uses: { type: 'number', nullable: true },
is_active: { type: 'number', nullable: true },
related_objects: {
kind: 'm:1',
entity: 'ObjectsModel',
joinColumn: 'owner_id'
},
related_items_item: {
kind: 'm:1',
entity: 'ItemsItemModel',
joinColumn: 'item_id'
}
},
});
schema._fkMappings = {
"owner_id": {
"relationKey": "related_objects",
"entityName": "ObjectsModel",
"referencedColumn": "id",
"nullable": false
},
"item_id": {
"relationKey": "related_items_item",
"entityName": "ItemsItemModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
ObjectsItemsInventoryModel,
entity: ObjectsItemsInventoryModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/objects-items-requirements-model.js
================================================
/**
*
* Reldens - ObjectsItemsRequirementsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ObjectsItemsRequirementsModel
{
constructor(id, object_id, item_key, required_item_key, required_quantity, auto_remove_requirement)
{
this.id = id;
this.object_id = object_id;
this.item_key = item_key;
this.required_item_key = required_item_key;
this.required_quantity = required_quantity;
this.auto_remove_requirement = auto_remove_requirement;
}
static createByProps(props)
{
const {id, object_id, item_key, required_item_key, required_quantity, auto_remove_requirement} = props;
return new this(id, object_id, item_key, required_item_key, required_quantity, auto_remove_requirement);
}
}
const schema = new EntitySchema({
class: ObjectsItemsRequirementsModel,
tableName: 'objects_items_requirements',
properties: {
id: { type: 'number', primary: true },
object_id: { type: 'number', persist: false },
item_key: { type: 'string', persist: false },
required_item_key: { type: 'string', persist: false },
required_quantity: { type: 'number', nullable: true },
auto_remove_requirement: { type: 'number', nullable: true },
related_objects: {
kind: 'm:1',
entity: 'ObjectsModel',
joinColumn: 'object_id'
},
related_items_item_item_key: {
kind: 'm:1',
entity: 'ItemsItemModel',
joinColumn: 'item_key'
},
related_items_item_required_item_key: {
kind: 'm:1',
entity: 'ItemsItemModel',
joinColumn: 'required_item_key'
}
},
});
schema._fkMappings = {
"object_id": {
"relationKey": "related_objects",
"entityName": "ObjectsModel",
"referencedColumn": "id",
"nullable": false
},
"item_key": {
"relationKey": "related_items_item_item_key",
"entityName": "ItemsItemModel",
"referencedColumn": "key",
"nullable": false
},
"required_item_key": {
"relationKey": "related_items_item_required_item_key",
"entityName": "ItemsItemModel",
"referencedColumn": "key",
"nullable": false
}
};
module.exports = {
ObjectsItemsRequirementsModel,
entity: ObjectsItemsRequirementsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/objects-items-rewards-model.js
================================================
/**
*
* Reldens - ObjectsItemsRewardsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ObjectsItemsRewardsModel
{
constructor(id, object_id, item_key, reward_item_key, reward_quantity, reward_item_is_required)
{
this.id = id;
this.object_id = object_id;
this.item_key = item_key;
this.reward_item_key = reward_item_key;
this.reward_quantity = reward_quantity;
this.reward_item_is_required = reward_item_is_required;
}
static createByProps(props)
{
const {id, object_id, item_key, reward_item_key, reward_quantity, reward_item_is_required} = props;
return new this(id, object_id, item_key, reward_item_key, reward_quantity, reward_item_is_required);
}
}
const schema = new EntitySchema({
class: ObjectsItemsRewardsModel,
tableName: 'objects_items_rewards',
properties: {
id: { type: 'number', primary: true },
object_id: { type: 'number', persist: false },
item_key: { type: 'string', persist: false },
reward_item_key: { type: 'string', persist: false },
reward_quantity: { type: 'number', nullable: true },
reward_item_is_required: { type: 'number', nullable: true },
related_objects: {
kind: 'm:1',
entity: 'ObjectsModel',
joinColumn: 'object_id'
},
related_items_item_item_key: {
kind: 'm:1',
entity: 'ItemsItemModel',
joinColumn: 'item_key'
},
related_items_item_reward_item_key: {
kind: 'm:1',
entity: 'ItemsItemModel',
joinColumn: 'reward_item_key'
}
},
});
schema._fkMappings = {
"object_id": {
"relationKey": "related_objects",
"entityName": "ObjectsModel",
"referencedColumn": "id",
"nullable": false
},
"item_key": {
"relationKey": "related_items_item_item_key",
"entityName": "ItemsItemModel",
"referencedColumn": "key",
"nullable": false
},
"reward_item_key": {
"relationKey": "related_items_item_reward_item_key",
"entityName": "ItemsItemModel",
"referencedColumn": "key",
"nullable": false
}
};
module.exports = {
ObjectsItemsRewardsModel,
entity: ObjectsItemsRewardsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/objects-model.js
================================================
/**
*
* Reldens - ObjectsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ObjectsModel
{
constructor(id, room_id, layer_name, tile_index, class_type, object_class_key, client_key, title, private_params, client_params, enabled, created_at, updated_at)
{
this.id = id;
this.room_id = room_id;
this.layer_name = layer_name;
this.tile_index = tile_index;
this.class_type = class_type;
this.object_class_key = object_class_key;
this.client_key = client_key;
this.title = title;
this.private_params = private_params;
this.client_params = client_params;
this.enabled = enabled;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, room_id, layer_name, tile_index, class_type, object_class_key, client_key, title, private_params, client_params, enabled, created_at, updated_at} = props;
return new this(id, room_id, layer_name, tile_index, class_type, object_class_key, client_key, title, private_params, client_params, enabled, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: ObjectsModel,
tableName: 'objects',
properties: {
id: { type: 'number', primary: true },
room_id: { type: 'number', persist: false },
layer_name: { type: 'string' },
tile_index: { type: 'number', nullable: true },
class_type: { type: 'number', persist: false },
object_class_key: { type: 'string' },
client_key: { type: 'string' },
title: { type: 'string', nullable: true },
private_params: { type: 'string', nullable: true },
client_params: { type: 'string', nullable: true },
enabled: { type: 'number', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_rooms: {
kind: 'm:1',
entity: 'RoomsModel',
joinColumn: 'room_id'
},
related_objects_types: {
kind: 'm:1',
entity: 'ObjectsTypesModel',
joinColumn: 'class_type'
},
related_objects_animations: {
kind: '1:m',
entity: 'ObjectsAnimationsModel',
mappedBy: 'related_objects'
},
related_objects_assets: {
kind: '1:m',
entity: 'ObjectsAssetsModel',
mappedBy: 'related_objects'
},
related_objects_items_inventory: {
kind: '1:m',
entity: 'ObjectsItemsInventoryModel',
mappedBy: 'related_objects'
},
related_objects_items_requirements: {
kind: '1:m',
entity: 'ObjectsItemsRequirementsModel',
mappedBy: 'related_objects'
},
related_objects_items_rewards: {
kind: '1:m',
entity: 'ObjectsItemsRewardsModel',
mappedBy: 'related_objects'
},
related_objects_skills: {
kind: '1:m',
entity: 'ObjectsSkillsModel',
mappedBy: 'related_objects'
},
related_objects_stats: {
kind: '1:m',
entity: 'ObjectsStatsModel',
mappedBy: 'related_objects'
},
related_respawn: {
kind: '1:m',
entity: 'RespawnModel',
mappedBy: 'related_objects'
},
related_rewards: {
kind: '1:m',
entity: 'RewardsModel',
mappedBy: 'related_objects'
}
},
});
schema._fkMappings = {
"room_id": {
"relationKey": "related_rooms",
"entityName": "RoomsModel",
"referencedColumn": "id",
"nullable": false
},
"class_type": {
"relationKey": "related_objects_types",
"entityName": "ObjectsTypesModel",
"referencedColumn": "id",
"nullable": true
}
};
module.exports = {
ObjectsModel,
entity: ObjectsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/objects-skills-model.js
================================================
/**
*
* Reldens - ObjectsSkillsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ObjectsSkillsModel
{
constructor(id, object_id, skill_id, target_id)
{
this.id = id;
this.object_id = object_id;
this.skill_id = skill_id;
this.target_id = target_id;
}
static createByProps(props)
{
const {id, object_id, skill_id, target_id} = props;
return new this(id, object_id, skill_id, target_id);
}
}
const schema = new EntitySchema({
class: ObjectsSkillsModel,
tableName: 'objects_skills',
properties: {
id: { type: 'number', primary: true },
object_id: { type: 'number', persist: false },
skill_id: { type: 'number', persist: false },
target_id: { type: 'number', persist: false },
related_objects: {
kind: 'm:1',
entity: 'ObjectsModel',
joinColumn: 'object_id'
},
related_skills_skill: {
kind: 'm:1',
entity: 'SkillsSkillModel',
joinColumn: 'skill_id'
},
related_target_options: {
kind: 'm:1',
entity: 'TargetOptionsModel',
joinColumn: 'target_id'
}
},
});
schema._fkMappings = {
"object_id": {
"relationKey": "related_objects",
"entityName": "ObjectsModel",
"referencedColumn": "id",
"nullable": false
},
"skill_id": {
"relationKey": "related_skills_skill",
"entityName": "SkillsSkillModel",
"referencedColumn": "id",
"nullable": false
},
"target_id": {
"relationKey": "related_target_options",
"entityName": "TargetOptionsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
ObjectsSkillsModel,
entity: ObjectsSkillsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/objects-stats-model.js
================================================
/**
*
* Reldens - ObjectsStatsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ObjectsStatsModel
{
constructor(id, object_id, stat_id, base_value, value)
{
this.id = id;
this.object_id = object_id;
this.stat_id = stat_id;
this.base_value = base_value;
this.value = value;
}
static createByProps(props)
{
const {id, object_id, stat_id, base_value, value} = props;
return new this(id, object_id, stat_id, base_value, value);
}
}
const schema = new EntitySchema({
class: ObjectsStatsModel,
tableName: 'objects_stats',
properties: {
id: { type: 'number', primary: true },
object_id: { type: 'number', persist: false },
stat_id: { type: 'number', persist: false },
base_value: { type: 'number' },
value: { type: 'number' },
related_objects: {
kind: 'm:1',
entity: 'ObjectsModel',
joinColumn: 'object_id'
},
related_stats: {
kind: 'm:1',
entity: 'StatsModel',
joinColumn: 'stat_id'
}
},
});
schema._fkMappings = {
"object_id": {
"relationKey": "related_objects",
"entityName": "ObjectsModel",
"referencedColumn": "id",
"nullable": false
},
"stat_id": {
"relationKey": "related_stats",
"entityName": "StatsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
ObjectsStatsModel,
entity: ObjectsStatsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/objects-types-model.js
================================================
/**
*
* Reldens - ObjectsTypesModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ObjectsTypesModel
{
constructor(id, key)
{
this.id = id;
this.key = key;
}
static createByProps(props)
{
const {id, key} = props;
return new this(id, key);
}
}
const schema = new EntitySchema({
class: ObjectsTypesModel,
tableName: 'objects_types',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
related_objects: {
kind: '1:m',
entity: 'ObjectsModel',
mappedBy: 'related_objects_types'
}
},
});
module.exports = {
ObjectsTypesModel,
entity: ObjectsTypesModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/operation-types-model.js
================================================
/**
*
* Reldens - OperationTypesModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class OperationTypesModel
{
constructor(id, label, key)
{
this.id = id;
this.label = label;
this.key = key;
}
static createByProps(props)
{
const {id, label, key} = props;
return new this(id, label, key);
}
}
const schema = new EntitySchema({
class: OperationTypesModel,
tableName: 'operation_types',
properties: {
id: { type: 'number', primary: true },
label: { type: 'string', nullable: true },
key: { type: 'number' },
related_clan_levels_modifiers: {
kind: '1:m',
entity: 'ClanLevelsModifiersModel',
mappedBy: 'related_operation_types'
},
related_items_item_modifiers: {
kind: '1:m',
entity: 'ItemsItemModifiersModel',
mappedBy: 'related_operation_types'
},
related_rewards_modifiers: {
kind: '1:m',
entity: 'RewardsModifiersModel',
mappedBy: 'related_operation_types'
},
related_skills_levels_modifiers: {
kind: '1:m',
entity: 'SkillsLevelsModifiersModel',
mappedBy: 'related_operation_types'
},
related_skills_skill_owner_effects: {
kind: '1:m',
entity: 'SkillsSkillOwnerEffectsModel',
mappedBy: 'related_operation_types'
},
related_skills_skill_target_effects: {
kind: '1:m',
entity: 'SkillsSkillTargetEffectsModel',
mappedBy: 'related_operation_types'
}
},
});
module.exports = {
OperationTypesModel,
entity: OperationTypesModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/players-model.js
================================================
/**
*
* Reldens - PlayersModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class PlayersModel
{
constructor(id, user_id, name, created_at, updated_at)
{
this.id = id;
this.user_id = user_id;
this.name = name;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, user_id, name, created_at, updated_at} = props;
return new this(id, user_id, name, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: PlayersModel,
tableName: 'players',
properties: {
id: { type: 'number', primary: true },
user_id: { type: 'number', persist: false },
name: { type: 'string' },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_users: {
kind: 'm:1',
entity: 'UsersModel',
joinColumn: 'user_id'
},
related_ads_played: {
kind: '1:m',
entity: 'AdsPlayedModel',
mappedBy: 'related_players'
},
related_audio_player_config: {
kind: '1:m',
entity: 'AudioPlayerConfigModel',
mappedBy: 'related_players'
},
related_chat_player: {
kind: '1:m',
entity: 'ChatModel',
mappedBy: 'related_players_player'
},
related_chat_private_player: {
kind: '1:m',
entity: 'ChatModel',
mappedBy: 'related_players_player'
},
related_clan: {
kind: '1:1',
entity: 'ClanModel',
mappedBy: 'related_players'
},
related_clan_members: {
kind: '1:1',
entity: 'ClanMembersModel',
mappedBy: 'related_players'
},
related_items_inventory: {
kind: '1:m',
entity: 'ItemsInventoryModel',
mappedBy: 'related_players'
},
related_players_state: {
kind: '1:1',
entity: 'PlayersStateModel',
mappedBy: 'related_players'
},
related_players_stats: {
kind: '1:m',
entity: 'PlayersStatsModel',
mappedBy: 'related_players'
},
related_rewards_events_state: {
kind: '1:m',
entity: 'RewardsEventsStateModel',
mappedBy: 'related_players'
},
related_scores: {
kind: '1:m',
entity: 'ScoresModel',
mappedBy: 'related_players'
},
related_scores_detail: {
kind: '1:m',
entity: 'ScoresDetailModel',
mappedBy: 'related_players'
},
related_skills_owners_class_path: {
kind: '1:m',
entity: 'SkillsOwnersClassPathModel',
mappedBy: 'related_players'
}
},
});
schema._fkMappings = {
"user_id": {
"relationKey": "related_users",
"entityName": "UsersModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
PlayersModel,
entity: PlayersModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/players-state-model.js
================================================
/**
*
* Reldens - PlayersStateModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class PlayersStateModel
{
constructor(id, player_id, room_id, x, y, dir)
{
this.id = id;
this.player_id = player_id;
this.room_id = room_id;
this.x = x;
this.y = y;
this.dir = dir;
}
static createByProps(props)
{
const {id, player_id, room_id, x, y, dir} = props;
return new this(id, player_id, room_id, x, y, dir);
}
}
const schema = new EntitySchema({
class: PlayersStateModel,
tableName: 'players_state',
properties: {
id: { type: 'number', primary: true },
player_id: { type: 'number', persist: false },
room_id: { type: 'number', persist: false },
x: { type: 'number' },
y: { type: 'number' },
dir: { type: 'string' },
related_players: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'player_id'
},
related_rooms: {
kind: 'm:1',
entity: 'RoomsModel',
joinColumn: 'room_id'
}
},
});
schema._fkMappings = {
"player_id": {
"relationKey": "related_players",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
},
"room_id": {
"relationKey": "related_rooms",
"entityName": "RoomsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
PlayersStateModel,
entity: PlayersStateModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/players-stats-model.js
================================================
/**
*
* Reldens - PlayersStatsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class PlayersStatsModel
{
constructor(id, player_id, stat_id, base_value, value)
{
this.id = id;
this.player_id = player_id;
this.stat_id = stat_id;
this.base_value = base_value;
this.value = value;
}
static createByProps(props)
{
const {id, player_id, stat_id, base_value, value} = props;
return new this(id, player_id, stat_id, base_value, value);
}
}
const schema = new EntitySchema({
class: PlayersStatsModel,
tableName: 'players_stats',
properties: {
id: { type: 'number', primary: true },
player_id: { type: 'number', persist: false },
stat_id: { type: 'number', persist: false },
base_value: { type: 'number' },
value: { type: 'number' },
related_players: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'player_id'
},
related_stats: {
kind: 'm:1',
entity: 'StatsModel',
joinColumn: 'stat_id'
}
},
});
schema._fkMappings = {
"player_id": {
"relationKey": "related_players",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
},
"stat_id": {
"relationKey": "related_stats",
"entityName": "StatsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
PlayersStatsModel,
entity: PlayersStatsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/registered-models-mikro-orm.js
================================================
/**
*
* Reldens - Registered Models
*
*/
const adsBannerModel = require('./ads-banner-model');
const adsModel = require('./ads-model');
const adsEventVideoModel = require('./ads-event-video-model');
const adsPlayedModel = require('./ads-played-model');
const adsProvidersModel = require('./ads-providers-model');
const adsTypesModel = require('./ads-types-model');
const audioCategoriesModel = require('./audio-categories-model');
const audioModel = require('./audio-model');
const audioMarkersModel = require('./audio-markers-model');
const audioPlayerConfigModel = require('./audio-player-config-model');
const chatModel = require('./chat-model');
const chatMessageTypesModel = require('./chat-message-types-model');
const clanModel = require('./clan-model');
const clanLevelsModel = require('./clan-levels-model');
const clanLevelsModifiersModel = require('./clan-levels-modifiers-model');
const clanMembersModel = require('./clan-members-model');
const configModel = require('./config-model');
const configTypesModel = require('./config-types-model');
const dropsAnimationsModel = require('./drops-animations-model');
const featuresModel = require('./features-model');
const itemsGroupModel = require('./items-group-model');
const itemsInventoryModel = require('./items-inventory-model');
const itemsItemModel = require('./items-item-model');
const itemsItemModifiersModel = require('./items-item-modifiers-model');
const itemsTypesModel = require('./items-types-model');
const localeModel = require('./locale-model');
const objectsAnimationsModel = require('./objects-animations-model');
const objectsAssetsModel = require('./objects-assets-model');
const objectsModel = require('./objects-model');
const objectsItemsInventoryModel = require('./objects-items-inventory-model');
const objectsItemsRequirementsModel = require('./objects-items-requirements-model');
const objectsItemsRewardsModel = require('./objects-items-rewards-model');
const objectsSkillsModel = require('./objects-skills-model');
const objectsStatsModel = require('./objects-stats-model');
const objectsTypesModel = require('./objects-types-model');
const operationTypesModel = require('./operation-types-model');
const playersModel = require('./players-model');
const playersStateModel = require('./players-state-model');
const playersStatsModel = require('./players-stats-model');
const respawnModel = require('./respawn-model');
const rewardsModel = require('./rewards-model');
const rewardsEventsModel = require('./rewards-events-model');
const rewardsEventsStateModel = require('./rewards-events-state-model');
const rewardsModifiersModel = require('./rewards-modifiers-model');
const roomsChangePointsModel = require('./rooms-change-points-model');
const roomsModel = require('./rooms-model');
const roomsReturnPointsModel = require('./rooms-return-points-model');
const scoresDetailModel = require('./scores-detail-model');
const scoresModel = require('./scores-model');
const skillsClassLevelUpAnimationsModel = require('./skills-class-level-up-animations-model');
const skillsClassPathModel = require('./skills-class-path-model');
const skillsClassPathLevelLabelsModel = require('./skills-class-path-level-labels-model');
const skillsClassPathLevelSkillsModel = require('./skills-class-path-level-skills-model');
const skillsGroupsModel = require('./skills-groups-model');
const skillsLevelsModel = require('./skills-levels-model');
const skillsLevelsModifiersConditionsModel = require('./skills-levels-modifiers-conditions-model');
const skillsLevelsModifiersModel = require('./skills-levels-modifiers-model');
const skillsLevelsSetModel = require('./skills-levels-set-model');
const skillsOwnersClassPathModel = require('./skills-owners-class-path-model');
const skillsSkillAnimationsModel = require('./skills-skill-animations-model');
const skillsSkillAttackModel = require('./skills-skill-attack-model');
const skillsSkillModel = require('./skills-skill-model');
const skillsSkillGroupRelationModel = require('./skills-skill-group-relation-model');
const skillsSkillOwnerConditionsModel = require('./skills-skill-owner-conditions-model');
const skillsSkillOwnerEffectsConditionsModel = require('./skills-skill-owner-effects-conditions-model');
const skillsSkillOwnerEffectsModel = require('./skills-skill-owner-effects-model');
const skillsSkillPhysicalDataModel = require('./skills-skill-physical-data-model');
const skillsSkillTargetEffectsConditionsModel = require('./skills-skill-target-effects-conditions-model');
const skillsSkillTargetEffectsModel = require('./skills-skill-target-effects-model');
const skillsSkillTypeModel = require('./skills-skill-type-model');
const snippetsModel = require('./snippets-model');
const statsModel = require('./stats-model');
const targetOptionsModel = require('./target-options-model');
const usersModel = require('./users-model');
const usersLocaleModel = require('./users-locale-model');
const usersLoginModel = require('./users-login-model');
const { entitiesConfig } = require('../../entities-config');
const { entitiesTranslations } = require('../../entities-translations');
let rawRegisteredEntities = {
adsBanner: adsBannerModel,
ads: adsModel,
adsEventVideo: adsEventVideoModel,
adsPlayed: adsPlayedModel,
adsProviders: adsProvidersModel,
adsTypes: adsTypesModel,
audioCategories: audioCategoriesModel,
audio: audioModel,
audioMarkers: audioMarkersModel,
audioPlayerConfig: audioPlayerConfigModel,
chat: chatModel,
chatMessageTypes: chatMessageTypesModel,
clan: clanModel,
clanLevels: clanLevelsModel,
clanLevelsModifiers: clanLevelsModifiersModel,
clanMembers: clanMembersModel,
config: configModel,
configTypes: configTypesModel,
dropsAnimations: dropsAnimationsModel,
features: featuresModel,
itemsGroup: itemsGroupModel,
itemsInventory: itemsInventoryModel,
itemsItem: itemsItemModel,
itemsItemModifiers: itemsItemModifiersModel,
itemsTypes: itemsTypesModel,
locale: localeModel,
objectsAnimations: objectsAnimationsModel,
objectsAssets: objectsAssetsModel,
objects: objectsModel,
objectsItemsInventory: objectsItemsInventoryModel,
objectsItemsRequirements: objectsItemsRequirementsModel,
objectsItemsRewards: objectsItemsRewardsModel,
objectsSkills: objectsSkillsModel,
objectsStats: objectsStatsModel,
objectsTypes: objectsTypesModel,
operationTypes: operationTypesModel,
players: playersModel,
playersState: playersStateModel,
playersStats: playersStatsModel,
respawn: respawnModel,
rewards: rewardsModel,
rewardsEvents: rewardsEventsModel,
rewardsEventsState: rewardsEventsStateModel,
rewardsModifiers: rewardsModifiersModel,
roomsChangePoints: roomsChangePointsModel,
rooms: roomsModel,
roomsReturnPoints: roomsReturnPointsModel,
scoresDetail: scoresDetailModel,
scores: scoresModel,
skillsClassLevelUpAnimations: skillsClassLevelUpAnimationsModel,
skillsClassPath: skillsClassPathModel,
skillsClassPathLevelLabels: skillsClassPathLevelLabelsModel,
skillsClassPathLevelSkills: skillsClassPathLevelSkillsModel,
skillsGroups: skillsGroupsModel,
skillsLevels: skillsLevelsModel,
skillsLevelsModifiersConditions: skillsLevelsModifiersConditionsModel,
skillsLevelsModifiers: skillsLevelsModifiersModel,
skillsLevelsSet: skillsLevelsSetModel,
skillsOwnersClassPath: skillsOwnersClassPathModel,
skillsSkillAnimations: skillsSkillAnimationsModel,
skillsSkillAttack: skillsSkillAttackModel,
skillsSkill: skillsSkillModel,
skillsSkillGroupRelation: skillsSkillGroupRelationModel,
skillsSkillOwnerConditions: skillsSkillOwnerConditionsModel,
skillsSkillOwnerEffectsConditions: skillsSkillOwnerEffectsConditionsModel,
skillsSkillOwnerEffects: skillsSkillOwnerEffectsModel,
skillsSkillPhysicalData: skillsSkillPhysicalDataModel,
skillsSkillTargetEffectsConditions: skillsSkillTargetEffectsConditionsModel,
skillsSkillTargetEffects: skillsSkillTargetEffectsModel,
skillsSkillType: skillsSkillTypeModel,
snippets: snippetsModel,
stats: statsModel,
targetOptions: targetOptionsModel,
users: usersModel,
usersLocale: usersLocaleModel,
usersLogin: usersLoginModel
};
module.exports.rawRegisteredEntities = rawRegisteredEntities;
module.exports.entitiesConfig = entitiesConfig;
module.exports.entitiesTranslations = entitiesTranslations;
================================================
FILE: generated-entities/models/mikro-orm/respawn-model.js
================================================
/**
*
* Reldens - RespawnModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class RespawnModel
{
constructor(id, object_id, respawn_time, instances_limit, layer, created_at, updated_at)
{
this.id = id;
this.object_id = object_id;
this.respawn_time = respawn_time;
this.instances_limit = instances_limit;
this.layer = layer;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, object_id, respawn_time, instances_limit, layer, created_at, updated_at} = props;
return new this(id, object_id, respawn_time, instances_limit, layer, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: RespawnModel,
tableName: 'respawn',
properties: {
id: { type: 'number', primary: true },
object_id: { type: 'number', persist: false },
respawn_time: { type: 'number', nullable: true },
instances_limit: { type: 'number', nullable: true },
layer: { type: 'string' },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_objects: {
kind: 'm:1',
entity: 'ObjectsModel',
joinColumn: 'object_id'
}
},
});
schema._fkMappings = {
"object_id": {
"relationKey": "related_objects",
"entityName": "ObjectsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
RespawnModel,
entity: RespawnModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/rewards-events-model.js
================================================
/**
*
* Reldens - RewardsEventsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class RewardsEventsModel
{
constructor(id, label, description, handler_key, event_key, event_data, position, enabled, active_from, active_to)
{
this.id = id;
this.label = label;
this.description = description;
this.handler_key = handler_key;
this.event_key = event_key;
this.event_data = event_data;
this.position = position;
this.enabled = enabled;
this.active_from = active_from;
this.active_to = active_to;
}
static createByProps(props)
{
const {id, label, description, handler_key, event_key, event_data, position, enabled, active_from, active_to} = props;
return new this(id, label, description, handler_key, event_key, event_data, position, enabled, active_from, active_to);
}
}
const schema = new EntitySchema({
class: RewardsEventsModel,
tableName: 'rewards_events',
properties: {
id: { type: 'number', primary: true },
label: { type: 'string' },
description: { type: 'string', nullable: true },
handler_key: { type: 'string' },
event_key: { type: 'string' },
event_data: { type: 'string' },
position: { type: 'number', nullable: true },
enabled: { type: 'number', nullable: true },
active_from: { type: 'Date', nullable: true },
active_to: { type: 'Date', nullable: true },
related_rewards_events_state: {
kind: '1:m',
entity: 'RewardsEventsStateModel',
mappedBy: 'related_rewards_events'
}
},
});
module.exports = {
RewardsEventsModel,
entity: RewardsEventsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/rewards-events-state-model.js
================================================
/**
*
* Reldens - RewardsEventsStateModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class RewardsEventsStateModel
{
constructor(id, rewards_events_id, player_id, state)
{
this.id = id;
this.rewards_events_id = rewards_events_id;
this.player_id = player_id;
this.state = state;
}
static createByProps(props)
{
const {id, rewards_events_id, player_id, state} = props;
return new this(id, rewards_events_id, player_id, state);
}
}
const schema = new EntitySchema({
class: RewardsEventsStateModel,
tableName: 'rewards_events_state',
properties: {
id: { type: 'number', primary: true },
rewards_events_id: { type: 'number', persist: false },
player_id: { type: 'number', persist: false },
state: { type: 'string', nullable: true },
related_rewards_events: {
kind: 'm:1',
entity: 'RewardsEventsModel',
joinColumn: 'rewards_events_id'
},
related_players: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'player_id'
}
},
});
schema._fkMappings = {
"rewards_events_id": {
"relationKey": "related_rewards_events",
"entityName": "RewardsEventsModel",
"referencedColumn": "id",
"nullable": false
},
"player_id": {
"relationKey": "related_players",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
RewardsEventsStateModel,
entity: RewardsEventsStateModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/rewards-model.js
================================================
/**
*
* Reldens - RewardsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class RewardsModel
{
constructor(id, object_id, item_id, modifier_id, experience, drop_rate, drop_quantity, is_unique, was_given, has_drop_body, created_at, updated_at)
{
this.id = id;
this.object_id = object_id;
this.item_id = item_id;
this.modifier_id = modifier_id;
this.experience = experience;
this.drop_rate = drop_rate;
this.drop_quantity = drop_quantity;
this.is_unique = is_unique;
this.was_given = was_given;
this.has_drop_body = has_drop_body;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, object_id, item_id, modifier_id, experience, drop_rate, drop_quantity, is_unique, was_given, has_drop_body, created_at, updated_at} = props;
return new this(id, object_id, item_id, modifier_id, experience, drop_rate, drop_quantity, is_unique, was_given, has_drop_body, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: RewardsModel,
tableName: 'rewards',
properties: {
id: { type: 'number', primary: true },
object_id: { type: 'number', persist: false },
item_id: { type: 'number', persist: false },
modifier_id: { type: 'number', persist: false },
experience: { type: 'number', nullable: true },
drop_rate: { type: 'number' },
drop_quantity: { type: 'number' },
is_unique: { type: 'number', nullable: true },
was_given: { type: 'number', nullable: true },
has_drop_body: { type: 'number', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_objects: {
kind: 'm:1',
entity: 'ObjectsModel',
joinColumn: 'object_id'
},
related_items_item: {
kind: 'm:1',
entity: 'ItemsItemModel',
joinColumn: 'item_id'
},
related_rewards_modifiers: {
kind: 'm:1',
entity: 'RewardsModifiersModel',
joinColumn: 'modifier_id'
}
},
});
schema._fkMappings = {
"object_id": {
"relationKey": "related_objects",
"entityName": "ObjectsModel",
"referencedColumn": "id",
"nullable": false
},
"item_id": {
"relationKey": "related_items_item",
"entityName": "ItemsItemModel",
"referencedColumn": "id",
"nullable": true
},
"modifier_id": {
"relationKey": "related_rewards_modifiers",
"entityName": "RewardsModifiersModel",
"referencedColumn": "id",
"nullable": true
}
};
module.exports = {
RewardsModel,
entity: RewardsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/rewards-modifiers-model.js
================================================
/**
*
* Reldens - RewardsModifiersModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class RewardsModifiersModel
{
constructor(id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty)
{
this.id = id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.minProperty = minProperty;
this.maxProperty = maxProperty;
}
static createByProps(props)
{
const {id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty} = props;
return new this(id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty);
}
}
const schema = new EntitySchema({
class: RewardsModifiersModel,
tableName: 'rewards_modifiers',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
property_key: { type: 'string' },
operation: { type: 'number', persist: false },
value: { type: 'string' },
minValue: { type: 'string', nullable: true },
maxValue: { type: 'string', nullable: true },
minProperty: { type: 'string', nullable: true },
maxProperty: { type: 'string', nullable: true },
related_operation_types: {
kind: 'm:1',
entity: 'OperationTypesModel',
joinColumn: 'operation'
},
related_rewards: {
kind: '1:m',
entity: 'RewardsModel',
mappedBy: 'related_rewards_modifiers'
}
},
});
schema._fkMappings = {
"operation": {
"relationKey": "related_operation_types",
"entityName": "OperationTypesModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
RewardsModifiersModel,
entity: RewardsModifiersModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/rooms-change-points-model.js
================================================
/**
*
* Reldens - RoomsChangePointsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class RoomsChangePointsModel
{
constructor(id, room_id, tile_index, next_room_id)
{
this.id = id;
this.room_id = room_id;
this.tile_index = tile_index;
this.next_room_id = next_room_id;
}
static createByProps(props)
{
const {id, room_id, tile_index, next_room_id} = props;
return new this(id, room_id, tile_index, next_room_id);
}
}
const schema = new EntitySchema({
class: RoomsChangePointsModel,
tableName: 'rooms_change_points',
properties: {
id: { type: 'number', primary: true },
room_id: { type: 'number', persist: false },
tile_index: { type: 'number' },
next_room_id: { type: 'number', persist: false },
related_rooms_room: {
kind: 'm:1',
entity: 'RoomsModel',
joinColumn: 'room_id'
},
related_rooms_next_room: {
kind: 'm:1',
entity: 'RoomsModel',
joinColumn: 'next_room_id'
}
},
});
schema._fkMappings = {
"room_id": {
"relationKey": "related_rooms_room",
"entityName": "RoomsModel",
"referencedColumn": "id",
"nullable": false
},
"next_room_id": {
"relationKey": "related_rooms_next_room",
"entityName": "RoomsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
RoomsChangePointsModel,
entity: RoomsChangePointsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/rooms-model.js
================================================
/**
*
* Reldens - RoomsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class RoomsModel
{
constructor(id, name, title, map_filename, scene_images, room_class_key, server_url, customData, created_at, updated_at)
{
this.id = id;
this.name = name;
this.title = title;
this.map_filename = map_filename;
this.scene_images = scene_images;
this.room_class_key = room_class_key;
this.server_url = server_url;
this.customData = customData;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, name, title, map_filename, scene_images, room_class_key, server_url, customData, created_at, updated_at} = props;
return new this(id, name, title, map_filename, scene_images, room_class_key, server_url, customData, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: RoomsModel,
tableName: 'rooms',
properties: {
id: { type: 'number', primary: true },
name: { type: 'string' },
title: { type: 'string' },
map_filename: { type: 'string' },
scene_images: { type: 'string' },
room_class_key: { type: 'string', nullable: true },
server_url: { type: 'string', nullable: true },
customData: { type: 'string', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_audio: {
kind: '1:m',
entity: 'AudioModel',
mappedBy: 'related_rooms'
},
related_chat: {
kind: '1:m',
entity: 'ChatModel',
mappedBy: 'related_rooms'
},
related_objects: {
kind: '1:m',
entity: 'ObjectsModel',
mappedBy: 'related_rooms'
},
related_players_state: {
kind: '1:m',
entity: 'PlayersStateModel',
mappedBy: 'related_rooms'
},
related_rooms_change_points_room: {
kind: '1:m',
entity: 'RoomsChangePointsModel',
mappedBy: 'related_rooms_room'
},
related_rooms_change_points_next_room: {
kind: '1:m',
entity: 'RoomsChangePointsModel',
mappedBy: 'related_rooms_room'
},
related_rooms_return_points_room: {
kind: '1:m',
entity: 'RoomsReturnPointsModel',
mappedBy: 'related_rooms_room'
},
related_rooms_return_points_from_room: {
kind: '1:m',
entity: 'RoomsReturnPointsModel',
mappedBy: 'related_rooms_room'
}
},
});
module.exports = {
RoomsModel,
entity: RoomsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/rooms-return-points-model.js
================================================
/**
*
* Reldens - RoomsReturnPointsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class RoomsReturnPointsModel
{
constructor(id, room_id, direction, x, y, is_default, from_room_id)
{
this.id = id;
this.room_id = room_id;
this.direction = direction;
this.x = x;
this.y = y;
this.is_default = is_default;
this.from_room_id = from_room_id;
}
static createByProps(props)
{
const {id, room_id, direction, x, y, is_default, from_room_id} = props;
return new this(id, room_id, direction, x, y, is_default, from_room_id);
}
}
const schema = new EntitySchema({
class: RoomsReturnPointsModel,
tableName: 'rooms_return_points',
properties: {
id: { type: 'number', primary: true },
room_id: { type: 'number', persist: false },
direction: { type: 'string' },
x: { type: 'number' },
y: { type: 'number' },
is_default: { type: 'number', nullable: true },
from_room_id: { type: 'number', persist: false },
related_rooms_room: {
kind: 'm:1',
entity: 'RoomsModel',
joinColumn: 'room_id'
},
related_rooms_from_room: {
kind: 'm:1',
entity: 'RoomsModel',
joinColumn: 'from_room_id'
}
},
});
schema._fkMappings = {
"room_id": {
"relationKey": "related_rooms_room",
"entityName": "RoomsModel",
"referencedColumn": "id",
"nullable": false
},
"from_room_id": {
"relationKey": "related_rooms_from_room",
"entityName": "RoomsModel",
"referencedColumn": "id",
"nullable": true
}
};
module.exports = {
RoomsReturnPointsModel,
entity: RoomsReturnPointsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/scores-detail-model.js
================================================
/**
*
* Reldens - ScoresDetailModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ScoresDetailModel
{
constructor(id, player_id, obtained_score, kill_time, kill_player_id, kill_npc_id)
{
this.id = id;
this.player_id = player_id;
this.obtained_score = obtained_score;
this.kill_time = kill_time;
this.kill_player_id = kill_player_id;
this.kill_npc_id = kill_npc_id;
}
static createByProps(props)
{
const {id, player_id, obtained_score, kill_time, kill_player_id, kill_npc_id} = props;
return new this(id, player_id, obtained_score, kill_time, kill_player_id, kill_npc_id);
}
}
const schema = new EntitySchema({
class: ScoresDetailModel,
tableName: 'scores_detail',
properties: {
id: { type: 'number', primary: true },
player_id: { type: 'number', persist: false },
obtained_score: { type: 'number' },
kill_time: { type: 'Date', nullable: true },
kill_player_id: { type: 'number', nullable: true },
kill_npc_id: { type: 'number', nullable: true },
related_players: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'player_id'
}
},
});
schema._fkMappings = {
"player_id": {
"relationKey": "related_players",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
ScoresDetailModel,
entity: ScoresDetailModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/scores-model.js
================================================
/**
*
* Reldens - ScoresModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class ScoresModel
{
constructor(id, player_id, total_score, players_kills_count, npcs_kills_count, last_player_kill_time, last_npc_kill_time, created_at, updated_at)
{
this.id = id;
this.player_id = player_id;
this.total_score = total_score;
this.players_kills_count = players_kills_count;
this.npcs_kills_count = npcs_kills_count;
this.last_player_kill_time = last_player_kill_time;
this.last_npc_kill_time = last_npc_kill_time;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, player_id, total_score, players_kills_count, npcs_kills_count, last_player_kill_time, last_npc_kill_time, created_at, updated_at} = props;
return new this(id, player_id, total_score, players_kills_count, npcs_kills_count, last_player_kill_time, last_npc_kill_time, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: ScoresModel,
tableName: 'scores',
properties: {
id: { type: 'number', primary: true },
player_id: { type: 'number', persist: false },
total_score: { type: 'number' },
players_kills_count: { type: 'number' },
npcs_kills_count: { type: 'number' },
last_player_kill_time: { type: 'Date', nullable: true },
last_npc_kill_time: { type: 'Date', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_players: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'player_id'
}
},
});
schema._fkMappings = {
"player_id": {
"relationKey": "related_players",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
ScoresModel,
entity: ScoresModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-class-level-up-animations-model.js
================================================
/**
*
* Reldens - SkillsClassLevelUpAnimationsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsClassLevelUpAnimationsModel
{
constructor(id, class_path_id, level_id, animationData)
{
this.id = id;
this.class_path_id = class_path_id;
this.level_id = level_id;
this.animationData = animationData;
}
static createByProps(props)
{
const {id, class_path_id, level_id, animationData} = props;
return new this(id, class_path_id, level_id, animationData);
}
}
const schema = new EntitySchema({
class: SkillsClassLevelUpAnimationsModel,
tableName: 'skills_class_level_up_animations',
properties: {
id: { type: 'number', primary: true },
class_path_id: { type: 'number', persist: false },
level_id: { type: 'number', persist: false },
animationData: { type: 'string' },
related_skills_class_path: {
kind: 'm:1',
entity: 'SkillsClassPathModel',
joinColumn: 'class_path_id'
},
related_skills_levels: {
kind: 'm:1',
entity: 'SkillsLevelsModel',
joinColumn: 'level_id'
}
},
});
schema._fkMappings = {
"class_path_id": {
"relationKey": "related_skills_class_path",
"entityName": "SkillsClassPathModel",
"referencedColumn": "id",
"nullable": true
},
"level_id": {
"relationKey": "related_skills_levels",
"entityName": "SkillsLevelsModel",
"referencedColumn": "id",
"nullable": true
}
};
module.exports = {
SkillsClassLevelUpAnimationsModel,
entity: SkillsClassLevelUpAnimationsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-class-path-level-labels-model.js
================================================
/**
*
* Reldens - SkillsClassPathLevelLabelsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsClassPathLevelLabelsModel
{
constructor(id, class_path_id, level_id, label)
{
this.id = id;
this.class_path_id = class_path_id;
this.level_id = level_id;
this.label = label;
}
static createByProps(props)
{
const {id, class_path_id, level_id, label} = props;
return new this(id, class_path_id, level_id, label);
}
}
const schema = new EntitySchema({
class: SkillsClassPathLevelLabelsModel,
tableName: 'skills_class_path_level_labels',
properties: {
id: { type: 'number', primary: true },
class_path_id: { type: 'number', persist: false },
level_id: { type: 'number', persist: false },
label: { type: 'string' },
related_skills_class_path: {
kind: 'm:1',
entity: 'SkillsClassPathModel',
joinColumn: 'class_path_id'
},
related_skills_levels: {
kind: 'm:1',
entity: 'SkillsLevelsModel',
joinColumn: 'level_id'
}
},
});
schema._fkMappings = {
"class_path_id": {
"relationKey": "related_skills_class_path",
"entityName": "SkillsClassPathModel",
"referencedColumn": "id",
"nullable": false
},
"level_id": {
"relationKey": "related_skills_levels",
"entityName": "SkillsLevelsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsClassPathLevelLabelsModel,
entity: SkillsClassPathLevelLabelsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-class-path-level-skills-model.js
================================================
/**
*
* Reldens - SkillsClassPathLevelSkillsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsClassPathLevelSkillsModel
{
constructor(id, class_path_id, level_id, skill_id)
{
this.id = id;
this.class_path_id = class_path_id;
this.level_id = level_id;
this.skill_id = skill_id;
}
static createByProps(props)
{
const {id, class_path_id, level_id, skill_id} = props;
return new this(id, class_path_id, level_id, skill_id);
}
}
const schema = new EntitySchema({
class: SkillsClassPathLevelSkillsModel,
tableName: 'skills_class_path_level_skills',
properties: {
id: { type: 'number', primary: true },
class_path_id: { type: 'number', persist: false },
level_id: { type: 'number', persist: false },
skill_id: { type: 'number', persist: false },
related_skills_class_path: {
kind: 'm:1',
entity: 'SkillsClassPathModel',
joinColumn: 'class_path_id'
},
related_skills_levels: {
kind: 'm:1',
entity: 'SkillsLevelsModel',
joinColumn: 'level_id'
},
related_skills_skill: {
kind: 'm:1',
entity: 'SkillsSkillModel',
joinColumn: 'skill_id'
}
},
});
schema._fkMappings = {
"class_path_id": {
"relationKey": "related_skills_class_path",
"entityName": "SkillsClassPathModel",
"referencedColumn": "id",
"nullable": false
},
"level_id": {
"relationKey": "related_skills_levels",
"entityName": "SkillsLevelsModel",
"referencedColumn": "id",
"nullable": false
},
"skill_id": {
"relationKey": "related_skills_skill",
"entityName": "SkillsSkillModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsClassPathLevelSkillsModel,
entity: SkillsClassPathLevelSkillsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-class-path-model.js
================================================
/**
*
* Reldens - SkillsClassPathModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsClassPathModel
{
constructor(id, key, label, levels_set_id, enabled, created_at, updated_at)
{
this.id = id;
this.key = key;
this.label = label;
this.levels_set_id = levels_set_id;
this.enabled = enabled;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, key, label, levels_set_id, enabled, created_at, updated_at} = props;
return new this(id, key, label, levels_set_id, enabled, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: SkillsClassPathModel,
tableName: 'skills_class_path',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
label: { type: 'string', nullable: true },
levels_set_id: { type: 'number', persist: false },
enabled: { type: 'number', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_skills_levels_set: {
kind: 'm:1',
entity: 'SkillsLevelsSetModel',
joinColumn: 'levels_set_id'
},
related_skills_class_level_up_animations: {
kind: '1:m',
entity: 'SkillsClassLevelUpAnimationsModel',
mappedBy: 'related_skills_class_path'
},
related_skills_class_path_level_labels: {
kind: '1:m',
entity: 'SkillsClassPathLevelLabelsModel',
mappedBy: 'related_skills_class_path'
},
related_skills_class_path_level_skills: {
kind: '1:m',
entity: 'SkillsClassPathLevelSkillsModel',
mappedBy: 'related_skills_class_path'
},
related_skills_owners_class_path: {
kind: '1:m',
entity: 'SkillsOwnersClassPathModel',
mappedBy: 'related_skills_class_path'
}
},
});
schema._fkMappings = {
"levels_set_id": {
"relationKey": "related_skills_levels_set",
"entityName": "SkillsLevelsSetModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsClassPathModel,
entity: SkillsClassPathModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-groups-model.js
================================================
/**
*
* Reldens - SkillsGroupsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsGroupsModel
{
constructor(id, key, label, description, sort)
{
this.id = id;
this.key = key;
this.label = label;
this.description = description;
this.sort = sort;
}
static createByProps(props)
{
const {id, key, label, description, sort} = props;
return new this(id, key, label, description, sort);
}
}
const schema = new EntitySchema({
class: SkillsGroupsModel,
tableName: 'skills_groups',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
label: { type: 'string' },
description: { type: 'string' },
sort: { type: 'number', nullable: true },
related_skills_skill_group_relation: {
kind: '1:m',
entity: 'SkillsSkillGroupRelationModel',
mappedBy: 'related_skills_groups'
}
},
});
module.exports = {
SkillsGroupsModel,
entity: SkillsGroupsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-levels-model.js
================================================
/**
*
* Reldens - SkillsLevelsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsLevelsModel
{
constructor(id, key, label, required_experience, level_set_id)
{
this.id = id;
this.key = key;
this.label = label;
this.required_experience = required_experience;
this.level_set_id = level_set_id;
}
static createByProps(props)
{
const {id, key, label, required_experience, level_set_id} = props;
return new this(id, key, label, required_experience, level_set_id);
}
}
const schema = new EntitySchema({
class: SkillsLevelsModel,
tableName: 'skills_levels',
properties: {
id: { type: 'number', primary: true },
key: { type: 'number' },
label: { type: 'string' },
required_experience: { type: 'number', nullable: true },
level_set_id: { type: 'number', persist: false },
related_skills_levels_set: {
kind: 'm:1',
entity: 'SkillsLevelsSetModel',
joinColumn: 'level_set_id'
},
related_skills_class_level_up_animations: {
kind: '1:m',
entity: 'SkillsClassLevelUpAnimationsModel',
mappedBy: 'related_skills_levels'
},
related_skills_class_path_level_labels: {
kind: '1:m',
entity: 'SkillsClassPathLevelLabelsModel',
mappedBy: 'related_skills_levels'
},
related_skills_class_path_level_skills: {
kind: '1:m',
entity: 'SkillsClassPathLevelSkillsModel',
mappedBy: 'related_skills_levels'
},
related_skills_levels_modifiers: {
kind: '1:m',
entity: 'SkillsLevelsModifiersModel',
mappedBy: 'related_skills_levels'
}
},
});
schema._fkMappings = {
"level_set_id": {
"relationKey": "related_skills_levels_set",
"entityName": "SkillsLevelsSetModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsLevelsModel,
entity: SkillsLevelsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-levels-modifiers-conditions-model.js
================================================
/**
*
* Reldens - SkillsLevelsModifiersConditionsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsLevelsModifiersConditionsModel
{
constructor(id, levels_modifier_id, key, property_key, conditional, value)
{
this.id = id;
this.levels_modifier_id = levels_modifier_id;
this.key = key;
this.property_key = property_key;
this.conditional = conditional;
this.value = value;
}
static createByProps(props)
{
const {id, levels_modifier_id, key, property_key, conditional, value} = props;
return new this(id, levels_modifier_id, key, property_key, conditional, value);
}
}
const schema = new EntitySchema({
class: SkillsLevelsModifiersConditionsModel,
tableName: 'skills_levels_modifiers_conditions',
properties: {
id: { type: 'number', primary: true },
levels_modifier_id: { type: 'number' },
key: { type: 'string' },
property_key: { type: 'string' },
conditional: { type: 'undefined' },
value: { type: 'string' }
},
});
module.exports = {
SkillsLevelsModifiersConditionsModel,
entity: SkillsLevelsModifiersConditionsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-levels-modifiers-model.js
================================================
/**
*
* Reldens - SkillsLevelsModifiersModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsLevelsModifiersModel
{
constructor(id, level_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty)
{
this.id = id;
this.level_id = level_id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.minProperty = minProperty;
this.maxProperty = maxProperty;
}
static createByProps(props)
{
const {id, level_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty} = props;
return new this(id, level_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty);
}
}
const schema = new EntitySchema({
class: SkillsLevelsModifiersModel,
tableName: 'skills_levels_modifiers',
properties: {
id: { type: 'number', primary: true },
level_id: { type: 'number', persist: false },
key: { type: 'string' },
property_key: { type: 'string' },
operation: { type: 'number', persist: false },
value: { type: 'string' },
minValue: { type: 'string', nullable: true },
maxValue: { type: 'string', nullable: true },
minProperty: { type: 'string', nullable: true },
maxProperty: { type: 'string', nullable: true },
related_skills_levels: {
kind: 'm:1',
entity: 'SkillsLevelsModel',
joinColumn: 'level_id'
},
related_operation_types: {
kind: 'm:1',
entity: 'OperationTypesModel',
joinColumn: 'operation'
}
},
});
schema._fkMappings = {
"level_id": {
"relationKey": "related_skills_levels",
"entityName": "SkillsLevelsModel",
"referencedColumn": "id",
"nullable": false
},
"operation": {
"relationKey": "related_operation_types",
"entityName": "OperationTypesModel",
"referencedColumn": "key",
"nullable": false
}
};
module.exports = {
SkillsLevelsModifiersModel,
entity: SkillsLevelsModifiersModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-levels-set-model.js
================================================
/**
*
* Reldens - SkillsLevelsSetModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsLevelsSetModel
{
constructor(id, key, label, autoFillRanges, autoFillExperienceMultiplier, created_at, updated_at)
{
this.id = id;
this.key = key;
this.label = label;
this.autoFillRanges = autoFillRanges;
this.autoFillExperienceMultiplier = autoFillExperienceMultiplier;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, key, label, autoFillRanges, autoFillExperienceMultiplier, created_at, updated_at} = props;
return new this(id, key, label, autoFillRanges, autoFillExperienceMultiplier, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: SkillsLevelsSetModel,
tableName: 'skills_levels_set',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string', nullable: true },
label: { type: 'string', nullable: true },
autoFillRanges: { type: 'number', nullable: true },
autoFillExperienceMultiplier: { type: 'number', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_skills_class_path: {
kind: '1:m',
entity: 'SkillsClassPathModel',
mappedBy: 'related_skills_levels_set'
},
related_skills_levels: {
kind: '1:m',
entity: 'SkillsLevelsModel',
mappedBy: 'related_skills_levels_set'
}
},
});
module.exports = {
SkillsLevelsSetModel,
entity: SkillsLevelsSetModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-owners-class-path-model.js
================================================
/**
*
* Reldens - SkillsOwnersClassPathModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsOwnersClassPathModel
{
constructor(id, class_path_id, owner_id, currentLevel, currentExp)
{
this.id = id;
this.class_path_id = class_path_id;
this.owner_id = owner_id;
this.currentLevel = currentLevel;
this.currentExp = currentExp;
}
static createByProps(props)
{
const {id, class_path_id, owner_id, currentLevel, currentExp} = props;
return new this(id, class_path_id, owner_id, currentLevel, currentExp);
}
}
const schema = new EntitySchema({
class: SkillsOwnersClassPathModel,
tableName: 'skills_owners_class_path',
properties: {
id: { type: 'number', primary: true },
class_path_id: { type: 'number', persist: false },
owner_id: { type: 'number', persist: false },
currentLevel: { type: 'number', nullable: true },
currentExp: { type: 'number', nullable: true },
related_skills_class_path: {
kind: 'm:1',
entity: 'SkillsClassPathModel',
joinColumn: 'class_path_id'
},
related_players: {
kind: 'm:1',
entity: 'PlayersModel',
joinColumn: 'owner_id'
}
},
});
schema._fkMappings = {
"class_path_id": {
"relationKey": "related_skills_class_path",
"entityName": "SkillsClassPathModel",
"referencedColumn": "id",
"nullable": false
},
"owner_id": {
"relationKey": "related_players",
"entityName": "PlayersModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsOwnersClassPathModel,
entity: SkillsOwnersClassPathModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-skill-animations-model.js
================================================
/**
*
* Reldens - SkillsSkillAnimationsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsSkillAnimationsModel
{
constructor(id, skill_id, key, classKey, animationData)
{
this.id = id;
this.skill_id = skill_id;
this.key = key;
this.classKey = classKey;
this.animationData = animationData;
}
static createByProps(props)
{
const {id, skill_id, key, classKey, animationData} = props;
return new this(id, skill_id, key, classKey, animationData);
}
}
const schema = new EntitySchema({
class: SkillsSkillAnimationsModel,
tableName: 'skills_skill_animations',
properties: {
id: { type: 'number', primary: true },
skill_id: { type: 'number', persist: false },
key: { type: 'string' },
classKey: { type: 'string', nullable: true },
animationData: { type: 'string' },
related_skills_skill: {
kind: 'm:1',
entity: 'SkillsSkillModel',
joinColumn: 'skill_id'
}
},
});
schema._fkMappings = {
"skill_id": {
"relationKey": "related_skills_skill",
"entityName": "SkillsSkillModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsSkillAnimationsModel,
entity: SkillsSkillAnimationsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-skill-attack-model.js
================================================
/**
*
* Reldens - SkillsSkillAttackModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsSkillAttackModel
{
constructor(id, skill_id, affectedProperty, allowEffectBelowZero, hitDamage, applyDirectDamage, attackProperties, defenseProperties, aimProperties, dodgeProperties, dodgeFullEnabled, dodgeOverAimSuccess, damageAffected, criticalAffected)
{
this.id = id;
this.skill_id = skill_id;
this.affectedProperty = affectedProperty;
this.allowEffectBelowZero = allowEffectBelowZero;
this.hitDamage = hitDamage;
this.applyDirectDamage = applyDirectDamage;
this.attackProperties = attackProperties;
this.defenseProperties = defenseProperties;
this.aimProperties = aimProperties;
this.dodgeProperties = dodgeProperties;
this.dodgeFullEnabled = dodgeFullEnabled;
this.dodgeOverAimSuccess = dodgeOverAimSuccess;
this.damageAffected = damageAffected;
this.criticalAffected = criticalAffected;
}
static createByProps(props)
{
const {id, skill_id, affectedProperty, allowEffectBelowZero, hitDamage, applyDirectDamage, attackProperties, defenseProperties, aimProperties, dodgeProperties, dodgeFullEnabled, dodgeOverAimSuccess, damageAffected, criticalAffected} = props;
return new this(id, skill_id, affectedProperty, allowEffectBelowZero, hitDamage, applyDirectDamage, attackProperties, defenseProperties, aimProperties, dodgeProperties, dodgeFullEnabled, dodgeOverAimSuccess, damageAffected, criticalAffected);
}
}
const schema = new EntitySchema({
class: SkillsSkillAttackModel,
tableName: 'skills_skill_attack',
properties: {
id: { type: 'number', primary: true },
skill_id: { type: 'number', persist: false },
affectedProperty: { type: 'string' },
allowEffectBelowZero: { type: 'number', nullable: true },
hitDamage: { type: 'number' },
applyDirectDamage: { type: 'number', nullable: true },
attackProperties: { type: 'string', nullable: true },
defenseProperties: { type: 'string', nullable: true },
aimProperties: { type: 'string', nullable: true },
dodgeProperties: { type: 'string', nullable: true },
dodgeFullEnabled: { type: 'number', nullable: true },
dodgeOverAimSuccess: { type: 'number', nullable: true },
damageAffected: { type: 'number', nullable: true },
criticalAffected: { type: 'number', nullable: true },
related_skills_skill: {
kind: 'm:1',
entity: 'SkillsSkillModel',
joinColumn: 'skill_id'
}
},
});
schema._fkMappings = {
"skill_id": {
"relationKey": "related_skills_skill",
"entityName": "SkillsSkillModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsSkillAttackModel,
entity: SkillsSkillAttackModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-skill-group-relation-model.js
================================================
/**
*
* Reldens - SkillsSkillGroupRelationModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsSkillGroupRelationModel
{
constructor(id, skill_id, group_id)
{
this.id = id;
this.skill_id = skill_id;
this.group_id = group_id;
}
static createByProps(props)
{
const {id, skill_id, group_id} = props;
return new this(id, skill_id, group_id);
}
}
const schema = new EntitySchema({
class: SkillsSkillGroupRelationModel,
tableName: 'skills_skill_group_relation',
properties: {
id: { type: 'number', primary: true },
skill_id: { type: 'number', persist: false },
group_id: { type: 'number', persist: false },
related_skills_skill: {
kind: 'm:1',
entity: 'SkillsSkillModel',
joinColumn: 'skill_id'
},
related_skills_groups: {
kind: 'm:1',
entity: 'SkillsGroupsModel',
joinColumn: 'group_id'
}
},
});
schema._fkMappings = {
"skill_id": {
"relationKey": "related_skills_skill",
"entityName": "SkillsSkillModel",
"referencedColumn": "id",
"nullable": false
},
"group_id": {
"relationKey": "related_skills_groups",
"entityName": "SkillsGroupsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsSkillGroupRelationModel,
entity: SkillsSkillGroupRelationModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-skill-model.js
================================================
/**
*
* Reldens - SkillsSkillModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsSkillModel
{
constructor(id, key, type, label, autoValidation, skillDelay, castTime, usesLimit, range, rangeAutomaticValidation, rangePropertyX, rangePropertyY, rangeTargetPropertyX, rangeTargetPropertyY, allowSelfTarget, criticalChance, criticalMultiplier, criticalFixedValue, customData, created_at, updated_at)
{
this.id = id;
this.key = key;
this.type = type;
this.label = label;
this.autoValidation = autoValidation;
this.skillDelay = skillDelay;
this.castTime = castTime;
this.usesLimit = usesLimit;
this.range = range;
this.rangeAutomaticValidation = rangeAutomaticValidation;
this.rangePropertyX = rangePropertyX;
this.rangePropertyY = rangePropertyY;
this.rangeTargetPropertyX = rangeTargetPropertyX;
this.rangeTargetPropertyY = rangeTargetPropertyY;
this.allowSelfTarget = allowSelfTarget;
this.criticalChance = criticalChance;
this.criticalMultiplier = criticalMultiplier;
this.criticalFixedValue = criticalFixedValue;
this.customData = customData;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, key, type, label, autoValidation, skillDelay, castTime, usesLimit, range, rangeAutomaticValidation, rangePropertyX, rangePropertyY, rangeTargetPropertyX, rangeTargetPropertyY, allowSelfTarget, criticalChance, criticalMultiplier, criticalFixedValue, customData, created_at, updated_at} = props;
return new this(id, key, type, label, autoValidation, skillDelay, castTime, usesLimit, range, rangeAutomaticValidation, rangePropertyX, rangePropertyY, rangeTargetPropertyX, rangeTargetPropertyY, allowSelfTarget, criticalChance, criticalMultiplier, criticalFixedValue, customData, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: SkillsSkillModel,
tableName: 'skills_skill',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
type: { type: 'number', persist: false },
label: { type: 'string', nullable: true },
autoValidation: { type: 'number', nullable: true },
skillDelay: { type: 'number' },
castTime: { type: 'number' },
usesLimit: { type: 'number', nullable: true },
range: { type: 'number' },
rangeAutomaticValidation: { type: 'number', nullable: true },
rangePropertyX: { type: 'string' },
rangePropertyY: { type: 'string' },
rangeTargetPropertyX: { type: 'string', nullable: true },
rangeTargetPropertyY: { type: 'string', nullable: true },
allowSelfTarget: { type: 'number', nullable: true },
criticalChance: { type: 'number', nullable: true },
criticalMultiplier: { type: 'number', nullable: true },
criticalFixedValue: { type: 'number', nullable: true },
customData: { type: 'string', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_skills_skill_type: {
kind: 'm:1',
entity: 'SkillsSkillTypeModel',
joinColumn: 'type'
},
related_objects_skills: {
kind: '1:m',
entity: 'ObjectsSkillsModel',
mappedBy: 'related_skills_skill'
},
related_skills_class_path_level_skills: {
kind: '1:m',
entity: 'SkillsClassPathLevelSkillsModel',
mappedBy: 'related_skills_skill'
},
related_skills_skill_animations: {
kind: '1:m',
entity: 'SkillsSkillAnimationsModel',
mappedBy: 'related_skills_skill'
},
related_skills_skill_attack: {
kind: '1:1',
entity: 'SkillsSkillAttackModel',
mappedBy: 'related_skills_skill'
},
related_skills_skill_group_relation: {
kind: '1:1',
entity: 'SkillsSkillGroupRelationModel',
mappedBy: 'related_skills_skill'
},
related_skills_skill_owner_conditions: {
kind: '1:m',
entity: 'SkillsSkillOwnerConditionsModel',
mappedBy: 'related_skills_skill'
},
related_skills_skill_owner_effects: {
kind: '1:m',
entity: 'SkillsSkillOwnerEffectsModel',
mappedBy: 'related_skills_skill'
},
related_skills_skill_physical_data: {
kind: '1:1',
entity: 'SkillsSkillPhysicalDataModel',
mappedBy: 'related_skills_skill'
},
related_skills_skill_target_effects: {
kind: '1:m',
entity: 'SkillsSkillTargetEffectsModel',
mappedBy: 'related_skills_skill'
}
},
});
schema._fkMappings = {
"type": {
"relationKey": "related_skills_skill_type",
"entityName": "SkillsSkillTypeModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsSkillModel,
entity: SkillsSkillModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-skill-owner-conditions-model.js
================================================
/**
*
* Reldens - SkillsSkillOwnerConditionsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsSkillOwnerConditionsModel
{
constructor(id, skill_id, key, property_key, conditional, value)
{
this.id = id;
this.skill_id = skill_id;
this.key = key;
this.property_key = property_key;
this.conditional = conditional;
this.value = value;
}
static createByProps(props)
{
const {id, skill_id, key, property_key, conditional, value} = props;
return new this(id, skill_id, key, property_key, conditional, value);
}
}
const schema = new EntitySchema({
class: SkillsSkillOwnerConditionsModel,
tableName: 'skills_skill_owner_conditions',
properties: {
id: { type: 'number', primary: true },
skill_id: { type: 'number', persist: false },
key: { type: 'string' },
property_key: { type: 'string' },
conditional: { type: 'undefined' },
value: { type: 'string' },
related_skills_skill: {
kind: 'm:1',
entity: 'SkillsSkillModel',
joinColumn: 'skill_id'
}
},
});
schema._fkMappings = {
"skill_id": {
"relationKey": "related_skills_skill",
"entityName": "SkillsSkillModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsSkillOwnerConditionsModel,
entity: SkillsSkillOwnerConditionsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-skill-owner-effects-conditions-model.js
================================================
/**
*
* Reldens - SkillsSkillOwnerEffectsConditionsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsSkillOwnerEffectsConditionsModel
{
constructor(id, skill_owner_effect_id, key, property_key, conditional, value)
{
this.id = id;
this.skill_owner_effect_id = skill_owner_effect_id;
this.key = key;
this.property_key = property_key;
this.conditional = conditional;
this.value = value;
}
static createByProps(props)
{
const {id, skill_owner_effect_id, key, property_key, conditional, value} = props;
return new this(id, skill_owner_effect_id, key, property_key, conditional, value);
}
}
const schema = new EntitySchema({
class: SkillsSkillOwnerEffectsConditionsModel,
tableName: 'skills_skill_owner_effects_conditions',
properties: {
id: { type: 'number', primary: true },
skill_owner_effect_id: { type: 'number', persist: false },
key: { type: 'string' },
property_key: { type: 'string' },
conditional: { type: 'undefined' },
value: { type: 'string' },
related_skills_skill_owner_effects: {
kind: 'm:1',
entity: 'SkillsSkillOwnerEffectsModel',
joinColumn: 'skill_owner_effect_id'
}
},
});
schema._fkMappings = {
"skill_owner_effect_id": {
"relationKey": "related_skills_skill_owner_effects",
"entityName": "SkillsSkillOwnerEffectsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsSkillOwnerEffectsConditionsModel,
entity: SkillsSkillOwnerEffectsConditionsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-skill-owner-effects-model.js
================================================
/**
*
* Reldens - SkillsSkillOwnerEffectsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsSkillOwnerEffectsModel
{
constructor(id, skill_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty)
{
this.id = id;
this.skill_id = skill_id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.minProperty = minProperty;
this.maxProperty = maxProperty;
}
static createByProps(props)
{
const {id, skill_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty} = props;
return new this(id, skill_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty);
}
}
const schema = new EntitySchema({
class: SkillsSkillOwnerEffectsModel,
tableName: 'skills_skill_owner_effects',
properties: {
id: { type: 'number', primary: true },
skill_id: { type: 'number', persist: false },
key: { type: 'string' },
property_key: { type: 'string' },
operation: { type: 'number', persist: false },
value: { type: 'string' },
minValue: { type: 'string' },
maxValue: { type: 'string' },
minProperty: { type: 'string', nullable: true },
maxProperty: { type: 'string', nullable: true },
related_skills_skill: {
kind: 'm:1',
entity: 'SkillsSkillModel',
joinColumn: 'skill_id'
},
related_operation_types: {
kind: 'm:1',
entity: 'OperationTypesModel',
joinColumn: 'operation'
},
related_skills_skill_owner_effects_conditions: {
kind: '1:m',
entity: 'SkillsSkillOwnerEffectsConditionsModel',
mappedBy: 'related_skills_skill_owner_effects'
}
},
});
schema._fkMappings = {
"skill_id": {
"relationKey": "related_skills_skill",
"entityName": "SkillsSkillModel",
"referencedColumn": "id",
"nullable": false
},
"operation": {
"relationKey": "related_operation_types",
"entityName": "OperationTypesModel",
"referencedColumn": "key",
"nullable": false
}
};
module.exports = {
SkillsSkillOwnerEffectsModel,
entity: SkillsSkillOwnerEffectsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-skill-physical-data-model.js
================================================
/**
*
* Reldens - SkillsSkillPhysicalDataModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsSkillPhysicalDataModel
{
constructor(id, skill_id, magnitude, objectWidth, objectHeight, validateTargetOnHit)
{
this.id = id;
this.skill_id = skill_id;
this.magnitude = magnitude;
this.objectWidth = objectWidth;
this.objectHeight = objectHeight;
this.validateTargetOnHit = validateTargetOnHit;
}
static createByProps(props)
{
const {id, skill_id, magnitude, objectWidth, objectHeight, validateTargetOnHit} = props;
return new this(id, skill_id, magnitude, objectWidth, objectHeight, validateTargetOnHit);
}
}
const schema = new EntitySchema({
class: SkillsSkillPhysicalDataModel,
tableName: 'skills_skill_physical_data',
properties: {
id: { type: 'number', primary: true },
skill_id: { type: 'number', persist: false },
magnitude: { type: 'number' },
objectWidth: { type: 'number' },
objectHeight: { type: 'number' },
validateTargetOnHit: { type: 'number', nullable: true },
related_skills_skill: {
kind: 'm:1',
entity: 'SkillsSkillModel',
joinColumn: 'skill_id'
}
},
});
schema._fkMappings = {
"skill_id": {
"relationKey": "related_skills_skill",
"entityName": "SkillsSkillModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsSkillPhysicalDataModel,
entity: SkillsSkillPhysicalDataModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-skill-target-effects-conditions-model.js
================================================
/**
*
* Reldens - SkillsSkillTargetEffectsConditionsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsSkillTargetEffectsConditionsModel
{
constructor(id, skill_target_effect_id, key, property_key, conditional, value)
{
this.id = id;
this.skill_target_effect_id = skill_target_effect_id;
this.key = key;
this.property_key = property_key;
this.conditional = conditional;
this.value = value;
}
static createByProps(props)
{
const {id, skill_target_effect_id, key, property_key, conditional, value} = props;
return new this(id, skill_target_effect_id, key, property_key, conditional, value);
}
}
const schema = new EntitySchema({
class: SkillsSkillTargetEffectsConditionsModel,
tableName: 'skills_skill_target_effects_conditions',
properties: {
id: { type: 'number', primary: true },
skill_target_effect_id: { type: 'number', persist: false },
key: { type: 'string' },
property_key: { type: 'string' },
conditional: { type: 'undefined' },
value: { type: 'string' },
related_skills_skill_target_effects: {
kind: 'm:1',
entity: 'SkillsSkillTargetEffectsModel',
joinColumn: 'skill_target_effect_id'
}
},
});
schema._fkMappings = {
"skill_target_effect_id": {
"relationKey": "related_skills_skill_target_effects",
"entityName": "SkillsSkillTargetEffectsModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SkillsSkillTargetEffectsConditionsModel,
entity: SkillsSkillTargetEffectsConditionsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-skill-target-effects-model.js
================================================
/**
*
* Reldens - SkillsSkillTargetEffectsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsSkillTargetEffectsModel
{
constructor(id, skill_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty)
{
this.id = id;
this.skill_id = skill_id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.minProperty = minProperty;
this.maxProperty = maxProperty;
}
static createByProps(props)
{
const {id, skill_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty} = props;
return new this(id, skill_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty);
}
}
const schema = new EntitySchema({
class: SkillsSkillTargetEffectsModel,
tableName: 'skills_skill_target_effects',
properties: {
id: { type: 'number', primary: true },
skill_id: { type: 'number', persist: false },
key: { type: 'string' },
property_key: { type: 'string' },
operation: { type: 'number', persist: false },
value: { type: 'string' },
minValue: { type: 'string' },
maxValue: { type: 'string' },
minProperty: { type: 'string', nullable: true },
maxProperty: { type: 'string', nullable: true },
related_skills_skill: {
kind: 'm:1',
entity: 'SkillsSkillModel',
joinColumn: 'skill_id'
},
related_operation_types: {
kind: 'm:1',
entity: 'OperationTypesModel',
joinColumn: 'operation'
},
related_skills_skill_target_effects_conditions: {
kind: '1:m',
entity: 'SkillsSkillTargetEffectsConditionsModel',
mappedBy: 'related_skills_skill_target_effects'
}
},
});
schema._fkMappings = {
"skill_id": {
"relationKey": "related_skills_skill",
"entityName": "SkillsSkillModel",
"referencedColumn": "id",
"nullable": false
},
"operation": {
"relationKey": "related_operation_types",
"entityName": "OperationTypesModel",
"referencedColumn": "key",
"nullable": false
}
};
module.exports = {
SkillsSkillTargetEffectsModel,
entity: SkillsSkillTargetEffectsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/skills-skill-type-model.js
================================================
/**
*
* Reldens - SkillsSkillTypeModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SkillsSkillTypeModel
{
constructor(id, key)
{
this.id = id;
this.key = key;
}
static createByProps(props)
{
const {id, key} = props;
return new this(id, key);
}
}
const schema = new EntitySchema({
class: SkillsSkillTypeModel,
tableName: 'skills_skill_type',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
related_skills_skill: {
kind: '1:m',
entity: 'SkillsSkillModel',
mappedBy: 'related_skills_skill_type'
}
},
});
module.exports = {
SkillsSkillTypeModel,
entity: SkillsSkillTypeModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/snippets-model.js
================================================
/**
*
* Reldens - SnippetsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class SnippetsModel
{
constructor(id, locale_id, key, value, created_at, updated_at)
{
this.id = id;
this.locale_id = locale_id;
this.key = key;
this.value = value;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, locale_id, key, value, created_at, updated_at} = props;
return new this(id, locale_id, key, value, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: SnippetsModel,
tableName: 'snippets',
properties: {
id: { type: 'number', primary: true },
locale_id: { type: 'number', persist: false },
key: { type: 'string' },
value: { type: 'string' },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_locale: {
kind: 'm:1',
entity: 'LocaleModel',
joinColumn: 'locale_id'
}
},
});
schema._fkMappings = {
"locale_id": {
"relationKey": "related_locale",
"entityName": "LocaleModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
SnippetsModel,
entity: SnippetsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/stats-model.js
================================================
/**
*
* Reldens - StatsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class StatsModel
{
constructor(id, key, label, description, base_value, customData, created_at, updated_at)
{
this.id = id;
this.key = key;
this.label = label;
this.description = description;
this.base_value = base_value;
this.customData = customData;
this.created_at = created_at;
this.updated_at = updated_at;
}
static createByProps(props)
{
const {id, key, label, description, base_value, customData, created_at, updated_at} = props;
return new this(id, key, label, description, base_value, customData, created_at, updated_at);
}
}
const schema = new EntitySchema({
class: StatsModel,
tableName: 'stats',
properties: {
id: { type: 'number', primary: true },
key: { type: 'string' },
label: { type: 'string' },
description: { type: 'string' },
base_value: { type: 'number' },
customData: { type: 'string', nullable: true },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
related_objects_stats: {
kind: '1:m',
entity: 'ObjectsStatsModel',
mappedBy: 'related_stats'
},
related_players_stats: {
kind: '1:m',
entity: 'PlayersStatsModel',
mappedBy: 'related_stats'
}
},
});
module.exports = {
StatsModel,
entity: StatsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/target-options-model.js
================================================
/**
*
* Reldens - TargetOptionsModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class TargetOptionsModel
{
constructor(id, target_key, target_label)
{
this.id = id;
this.target_key = target_key;
this.target_label = target_label;
}
static createByProps(props)
{
const {id, target_key, target_label} = props;
return new this(id, target_key, target_label);
}
}
const schema = new EntitySchema({
class: TargetOptionsModel,
tableName: 'target_options',
properties: {
id: { type: 'number', primary: true },
target_key: { type: 'string' },
target_label: { type: 'string', nullable: true },
related_objects_skills: {
kind: '1:m',
entity: 'ObjectsSkillsModel',
mappedBy: 'related_target_options'
}
},
});
module.exports = {
TargetOptionsModel,
entity: TargetOptionsModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/users-locale-model.js
================================================
/**
*
* Reldens - UsersLocaleModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class UsersLocaleModel
{
constructor(id, locale_id, user_id)
{
this.id = id;
this.locale_id = locale_id;
this.user_id = user_id;
}
static createByProps(props)
{
const {id, locale_id, user_id} = props;
return new this(id, locale_id, user_id);
}
}
const schema = new EntitySchema({
class: UsersLocaleModel,
tableName: 'users_locale',
properties: {
id: { type: 'number', primary: true },
locale_id: { type: 'number', persist: false },
user_id: { type: 'number', persist: false },
related_locale: {
kind: 'm:1',
entity: 'LocaleModel',
joinColumn: 'locale_id'
},
related_users: {
kind: 'm:1',
entity: 'UsersModel',
joinColumn: 'user_id'
}
},
});
schema._fkMappings = {
"locale_id": {
"relationKey": "related_locale",
"entityName": "LocaleModel",
"referencedColumn": "id",
"nullable": true
},
"user_id": {
"relationKey": "related_users",
"entityName": "UsersModel",
"referencedColumn": "id",
"nullable": true
}
};
module.exports = {
UsersLocaleModel,
entity: UsersLocaleModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/users-login-model.js
================================================
/**
*
* Reldens - UsersLoginModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class UsersLoginModel
{
constructor(id, user_id, login_date, logout_date)
{
this.id = id;
this.user_id = user_id;
this.login_date = login_date;
this.logout_date = logout_date;
}
static createByProps(props)
{
const {id, user_id, login_date, logout_date} = props;
return new this(id, user_id, login_date, logout_date);
}
}
const schema = new EntitySchema({
class: UsersLoginModel,
tableName: 'users_login',
properties: {
id: { type: 'number', primary: true },
user_id: { type: 'number', persist: false },
login_date: { type: 'Date', nullable: true },
logout_date: { type: 'Date', nullable: true },
related_users: {
kind: 'm:1',
entity: 'UsersModel',
joinColumn: 'user_id'
}
},
});
schema._fkMappings = {
"user_id": {
"relationKey": "related_users",
"entityName": "UsersModel",
"referencedColumn": "id",
"nullable": false
}
};
module.exports = {
UsersLoginModel,
entity: UsersLoginModel,
schema: schema
};
================================================
FILE: generated-entities/models/mikro-orm/users-model.js
================================================
/**
*
* Reldens - UsersModel
*
*/
const { MikroOrmCore } = require('@reldens/storage');
const { EntitySchema } = MikroOrmCore;
class UsersModel
{
constructor(id, email, username, password, role_id, status, created_at, updated_at, played_time, login_count)
{
this.id = id;
this.email = email;
this.username = username;
this.password = password;
this.role_id = role_id;
this.status = status;
this.created_at = created_at;
this.updated_at = updated_at;
this.played_time = played_time;
this.login_count = login_count;
}
static createByProps(props)
{
const {id, email, username, password, role_id, status, created_at, updated_at, played_time, login_count} = props;
return new this(id, email, username, password, role_id, status, created_at, updated_at, played_time, login_count);
}
}
const schema = new EntitySchema({
class: UsersModel,
tableName: 'users',
properties: {
id: { type: 'number', primary: true },
email: { type: 'string' },
username: { type: 'string' },
password: { type: 'string' },
role_id: { type: 'number' },
status: { type: 'string' },
created_at: { type: 'Date', nullable: true },
updated_at: { type: 'Date', nullable: true },
played_time: { type: 'number', nullable: true },
login_count: { type: 'number', nullable: true },
related_players: {
kind: '1:m',
entity: 'PlayersModel',
mappedBy: 'related_users'
},
related_users_locale: {
kind: '1:m',
entity: 'UsersLocaleModel',
mappedBy: 'related_users'
},
related_users_login: {
kind: '1:m',
entity: 'UsersLoginModel',
mappedBy: 'related_users'
}
},
});
module.exports = {
UsersModel,
entity: UsersModel,
schema: schema
};
================================================
FILE: generated-entities/models/objection-js/ads-banner-model.js
================================================
/**
*
* Reldens - AdsBannerModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class AdsBannerModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'ads_banner';
}
static get relationMappings()
{
const { AdsModel } = require('./ads-model');
return {
related_ads: {
relation: this.BelongsToOneRelation,
modelClass: AdsModel,
join: {
from: this.tableName+'.ads_id',
to: AdsModel.tableName+'.id'
}
}
};
}
}
module.exports.AdsBannerModel = AdsBannerModel;
================================================
FILE: generated-entities/models/objection-js/ads-event-video-model.js
================================================
/**
*
* Reldens - AdsEventVideoModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class AdsEventVideoModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'ads_event_video';
}
static get relationMappings()
{
const { AdsModel } = require('./ads-model');
return {
related_ads: {
relation: this.BelongsToOneRelation,
modelClass: AdsModel,
join: {
from: this.tableName+'.ads_id',
to: AdsModel.tableName+'.id'
}
}
};
}
}
module.exports.AdsEventVideoModel = AdsEventVideoModel;
================================================
FILE: generated-entities/models/objection-js/ads-model.js
================================================
/**
*
* Reldens - AdsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class AdsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'ads';
}
static get relationMappings()
{
const { AdsProvidersModel } = require('./ads-providers-model');
const { AdsTypesModel } = require('./ads-types-model');
const { AdsBannerModel } = require('./ads-banner-model');
const { AdsEventVideoModel } = require('./ads-event-video-model');
const { AdsPlayedModel } = require('./ads-played-model');
return {
related_ads_providers: {
relation: this.BelongsToOneRelation,
modelClass: AdsProvidersModel,
join: {
from: this.tableName+'.provider_id',
to: AdsProvidersModel.tableName+'.id'
}
},
related_ads_types: {
relation: this.BelongsToOneRelation,
modelClass: AdsTypesModel,
join: {
from: this.tableName+'.type_id',
to: AdsTypesModel.tableName+'.id'
}
},
related_ads_banner: {
relation: this.HasOneRelation,
modelClass: AdsBannerModel,
join: {
from: this.tableName+'.id',
to: AdsBannerModel.tableName+'.ads_id'
}
},
related_ads_event_video: {
relation: this.HasOneRelation,
modelClass: AdsEventVideoModel,
join: {
from: this.tableName+'.id',
to: AdsEventVideoModel.tableName+'.ads_id'
}
},
related_ads_played: {
relation: this.HasManyRelation,
modelClass: AdsPlayedModel,
join: {
from: this.tableName+'.id',
to: AdsPlayedModel.tableName+'.ads_id'
}
}
};
}
}
module.exports.AdsModel = AdsModel;
================================================
FILE: generated-entities/models/objection-js/ads-played-model.js
================================================
/**
*
* Reldens - AdsPlayedModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class AdsPlayedModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'ads_played';
}
static get relationMappings()
{
const { AdsModel } = require('./ads-model');
const { PlayersModel } = require('./players-model');
return {
related_ads: {
relation: this.BelongsToOneRelation,
modelClass: AdsModel,
join: {
from: this.tableName+'.ads_id',
to: AdsModel.tableName+'.id'
}
},
related_players: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.player_id',
to: PlayersModel.tableName+'.id'
}
}
};
}
}
module.exports.AdsPlayedModel = AdsPlayedModel;
================================================
FILE: generated-entities/models/objection-js/ads-providers-model.js
================================================
/**
*
* Reldens - AdsProvidersModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class AdsProvidersModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'ads_providers';
}
static get relationMappings()
{
const { AdsModel } = require('./ads-model');
return {
related_ads: {
relation: this.HasManyRelation,
modelClass: AdsModel,
join: {
from: this.tableName+'.id',
to: AdsModel.tableName+'.provider_id'
}
}
};
}
}
module.exports.AdsProvidersModel = AdsProvidersModel;
================================================
FILE: generated-entities/models/objection-js/ads-types-model.js
================================================
/**
*
* Reldens - AdsTypesModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class AdsTypesModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'ads_types';
}
static get relationMappings()
{
const { AdsModel } = require('./ads-model');
return {
related_ads: {
relation: this.HasManyRelation,
modelClass: AdsModel,
join: {
from: this.tableName+'.id',
to: AdsModel.tableName+'.type_id'
}
}
};
}
}
module.exports.AdsTypesModel = AdsTypesModel;
================================================
FILE: generated-entities/models/objection-js/audio-categories-model.js
================================================
/**
*
* Reldens - AudioCategoriesModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class AudioCategoriesModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'audio_categories';
}
static get relationMappings()
{
const { AudioModel } = require('./audio-model');
const { AudioPlayerConfigModel } = require('./audio-player-config-model');
return {
related_audio: {
relation: this.HasManyRelation,
modelClass: AudioModel,
join: {
from: this.tableName+'.id',
to: AudioModel.tableName+'.category_id'
}
},
related_audio_player_config: {
relation: this.HasManyRelation,
modelClass: AudioPlayerConfigModel,
join: {
from: this.tableName+'.id',
to: AudioPlayerConfigModel.tableName+'.category_id'
}
}
};
}
}
module.exports.AudioCategoriesModel = AudioCategoriesModel;
================================================
FILE: generated-entities/models/objection-js/audio-markers-model.js
================================================
/**
*
* Reldens - AudioMarkersModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class AudioMarkersModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'audio_markers';
}
static get relationMappings()
{
const { AudioModel } = require('./audio-model');
return {
related_audio: {
relation: this.BelongsToOneRelation,
modelClass: AudioModel,
join: {
from: this.tableName+'.audio_id',
to: AudioModel.tableName+'.id'
}
}
};
}
}
module.exports.AudioMarkersModel = AudioMarkersModel;
================================================
FILE: generated-entities/models/objection-js/audio-model.js
================================================
/**
*
* Reldens - AudioModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class AudioModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'audio';
}
static get relationMappings()
{
const { RoomsModel } = require('./rooms-model');
const { AudioCategoriesModel } = require('./audio-categories-model');
const { AudioMarkersModel } = require('./audio-markers-model');
return {
related_rooms: {
relation: this.BelongsToOneRelation,
modelClass: RoomsModel,
join: {
from: this.tableName+'.room_id',
to: RoomsModel.tableName+'.id'
}
},
related_audio_categories: {
relation: this.BelongsToOneRelation,
modelClass: AudioCategoriesModel,
join: {
from: this.tableName+'.category_id',
to: AudioCategoriesModel.tableName+'.id'
}
},
related_audio_markers: {
relation: this.HasManyRelation,
modelClass: AudioMarkersModel,
join: {
from: this.tableName+'.id',
to: AudioMarkersModel.tableName+'.audio_id'
}
}
};
}
}
module.exports.AudioModel = AudioModel;
================================================
FILE: generated-entities/models/objection-js/audio-player-config-model.js
================================================
/**
*
* Reldens - AudioPlayerConfigModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class AudioPlayerConfigModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'audio_player_config';
}
static get relationMappings()
{
const { PlayersModel } = require('./players-model');
const { AudioCategoriesModel } = require('./audio-categories-model');
return {
related_players: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.player_id',
to: PlayersModel.tableName+'.id'
}
},
related_audio_categories: {
relation: this.BelongsToOneRelation,
modelClass: AudioCategoriesModel,
join: {
from: this.tableName+'.category_id',
to: AudioCategoriesModel.tableName+'.id'
}
}
};
}
}
module.exports.AudioPlayerConfigModel = AudioPlayerConfigModel;
================================================
FILE: generated-entities/models/objection-js/chat-message-types-model.js
================================================
/**
*
* Reldens - ChatMessageTypesModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ChatMessageTypesModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'chat_message_types';
}
static get relationMappings()
{
const { ChatMessageTypesModel } = require('./chat-message-types-model');
const { ChatModel } = require('./chat-model');
return {
related_chat_message_types: {
relation: this.BelongsToOneRelation,
modelClass: ChatMessageTypesModel,
join: {
from: this.tableName+'.also_show_in_type',
to: ChatMessageTypesModel.tableName+'.id'
}
},
related_chat: {
relation: this.HasManyRelation,
modelClass: ChatModel,
join: {
from: this.tableName+'.id',
to: ChatModel.tableName+'.message_type'
}
}
};
}
}
module.exports.ChatMessageTypesModel = ChatMessageTypesModel;
================================================
FILE: generated-entities/models/objection-js/chat-model.js
================================================
/**
*
* Reldens - ChatModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ChatModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'chat';
}
static get relationMappings()
{
const { PlayersModel } = require('./players-model');
const { RoomsModel } = require('./rooms-model');
const { ChatMessageTypesModel } = require('./chat-message-types-model');
return {
related_players_player: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.player_id',
to: PlayersModel.tableName+'.id'
}
},
related_rooms: {
relation: this.BelongsToOneRelation,
modelClass: RoomsModel,
join: {
from: this.tableName+'.room_id',
to: RoomsModel.tableName+'.id'
}
},
related_players_private_player: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.private_player_id',
to: PlayersModel.tableName+'.id'
}
},
related_chat_message_types: {
relation: this.BelongsToOneRelation,
modelClass: ChatMessageTypesModel,
join: {
from: this.tableName+'.message_type',
to: ChatMessageTypesModel.tableName+'.id'
}
}
};
}
}
module.exports.ChatModel = ChatModel;
================================================
FILE: generated-entities/models/objection-js/clan-levels-model.js
================================================
/**
*
* Reldens - ClanLevelsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ClanLevelsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'clan_levels';
}
static get relationMappings()
{
const { ClanModel } = require('./clan-model');
const { ClanLevelsModifiersModel } = require('./clan-levels-modifiers-model');
return {
related_clan: {
relation: this.HasManyRelation,
modelClass: ClanModel,
join: {
from: this.tableName+'.key',
to: ClanModel.tableName+'.level'
}
},
related_clan_levels_modifiers: {
relation: this.HasManyRelation,
modelClass: ClanLevelsModifiersModel,
join: {
from: this.tableName+'.id',
to: ClanLevelsModifiersModel.tableName+'.level_id'
}
}
};
}
}
module.exports.ClanLevelsModel = ClanLevelsModel;
================================================
FILE: generated-entities/models/objection-js/clan-levels-modifiers-model.js
================================================
/**
*
* Reldens - ClanLevelsModifiersModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ClanLevelsModifiersModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'clan_levels_modifiers';
}
static get relationMappings()
{
const { ClanLevelsModel } = require('./clan-levels-model');
const { OperationTypesModel } = require('./operation-types-model');
return {
related_clan_levels: {
relation: this.BelongsToOneRelation,
modelClass: ClanLevelsModel,
join: {
from: this.tableName+'.level_id',
to: ClanLevelsModel.tableName+'.id'
}
},
related_operation_types: {
relation: this.BelongsToOneRelation,
modelClass: OperationTypesModel,
join: {
from: this.tableName+'.operation',
to: OperationTypesModel.tableName+'.key'
}
}
};
}
}
module.exports.ClanLevelsModifiersModel = ClanLevelsModifiersModel;
================================================
FILE: generated-entities/models/objection-js/clan-members-model.js
================================================
/**
*
* Reldens - ClanMembersModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ClanMembersModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'clan_members';
}
static get relationMappings()
{
const { ClanModel } = require('./clan-model');
const { PlayersModel } = require('./players-model');
return {
related_clan: {
relation: this.BelongsToOneRelation,
modelClass: ClanModel,
join: {
from: this.tableName+'.clan_id',
to: ClanModel.tableName+'.id'
}
},
related_players: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.player_id',
to: PlayersModel.tableName+'.id'
}
}
};
}
}
module.exports.ClanMembersModel = ClanMembersModel;
================================================
FILE: generated-entities/models/objection-js/clan-model.js
================================================
/**
*
* Reldens - ClanModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ClanModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'clan';
}
static get relationMappings()
{
const { PlayersModel } = require('./players-model');
const { ClanLevelsModel } = require('./clan-levels-model');
const { ClanMembersModel } = require('./clan-members-model');
return {
related_players: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.owner_id',
to: PlayersModel.tableName+'.id'
}
},
related_clan_levels: {
relation: this.BelongsToOneRelation,
modelClass: ClanLevelsModel,
join: {
from: this.tableName+'.level',
to: ClanLevelsModel.tableName+'.key'
}
},
related_clan_members: {
relation: this.HasManyRelation,
modelClass: ClanMembersModel,
join: {
from: this.tableName+'.id',
to: ClanMembersModel.tableName+'.clan_id'
}
}
};
}
}
module.exports.ClanModel = ClanModel;
================================================
FILE: generated-entities/models/objection-js/config-model.js
================================================
/**
*
* Reldens - ConfigModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ConfigModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'config';
}
static get relationMappings()
{
const { ConfigTypesModel } = require('./config-types-model');
return {
related_config_types: {
relation: this.BelongsToOneRelation,
modelClass: ConfigTypesModel,
join: {
from: this.tableName+'.type',
to: ConfigTypesModel.tableName+'.id'
}
}
};
}
}
module.exports.ConfigModel = ConfigModel;
================================================
FILE: generated-entities/models/objection-js/config-types-model.js
================================================
/**
*
* Reldens - ConfigTypesModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ConfigTypesModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'config_types';
}
static get relationMappings()
{
const { ConfigModel } = require('./config-model');
return {
related_config: {
relation: this.HasManyRelation,
modelClass: ConfigModel,
join: {
from: this.tableName+'.id',
to: ConfigModel.tableName+'.type'
}
}
};
}
}
module.exports.ConfigTypesModel = ConfigTypesModel;
================================================
FILE: generated-entities/models/objection-js/drops-animations-model.js
================================================
/**
*
* Reldens - DropsAnimationsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class DropsAnimationsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'drops_animations';
}
static get relationMappings()
{
const { ItemsItemModel } = require('./items-item-model');
return {
related_items_item: {
relation: this.BelongsToOneRelation,
modelClass: ItemsItemModel,
join: {
from: this.tableName+'.item_id',
to: ItemsItemModel.tableName+'.id'
}
}
};
}
}
module.exports.DropsAnimationsModel = DropsAnimationsModel;
================================================
FILE: generated-entities/models/objection-js/features-model.js
================================================
/**
*
* Reldens - FeaturesModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class FeaturesModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'features';
}
}
module.exports.FeaturesModel = FeaturesModel;
================================================
FILE: generated-entities/models/objection-js/items-group-model.js
================================================
/**
*
* Reldens - ItemsGroupModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ItemsGroupModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'items_group';
}
static get relationMappings()
{
const { ItemsItemModel } = require('./items-item-model');
return {
related_items_item: {
relation: this.HasManyRelation,
modelClass: ItemsItemModel,
join: {
from: this.tableName+'.id',
to: ItemsItemModel.tableName+'.group_id'
}
}
};
}
}
module.exports.ItemsGroupModel = ItemsGroupModel;
================================================
FILE: generated-entities/models/objection-js/items-inventory-model.js
================================================
/**
*
* Reldens - ItemsInventoryModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ItemsInventoryModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'items_inventory';
}
static get relationMappings()
{
const { PlayersModel } = require('./players-model');
const { ItemsItemModel } = require('./items-item-model');
return {
related_players: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.owner_id',
to: PlayersModel.tableName+'.id'
}
},
related_items_item: {
relation: this.BelongsToOneRelation,
modelClass: ItemsItemModel,
join: {
from: this.tableName+'.item_id',
to: ItemsItemModel.tableName+'.id'
}
}
};
}
}
module.exports.ItemsInventoryModel = ItemsInventoryModel;
================================================
FILE: generated-entities/models/objection-js/items-item-model.js
================================================
/**
*
* Reldens - ItemsItemModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ItemsItemModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'items_item';
}
static get relationMappings()
{
const { ItemsTypesModel } = require('./items-types-model');
const { ItemsGroupModel } = require('./items-group-model');
const { DropsAnimationsModel } = require('./drops-animations-model');
const { ItemsInventoryModel } = require('./items-inventory-model');
const { ItemsItemModifiersModel } = require('./items-item-modifiers-model');
const { ObjectsItemsInventoryModel } = require('./objects-items-inventory-model');
const { ObjectsItemsRequirementsModel } = require('./objects-items-requirements-model');
const { ObjectsItemsRewardsModel } = require('./objects-items-rewards-model');
const { RewardsModel } = require('./rewards-model');
return {
related_items_types: {
relation: this.BelongsToOneRelation,
modelClass: ItemsTypesModel,
join: {
from: this.tableName+'.type',
to: ItemsTypesModel.tableName+'.id'
}
},
related_items_group: {
relation: this.BelongsToOneRelation,
modelClass: ItemsGroupModel,
join: {
from: this.tableName+'.group_id',
to: ItemsGroupModel.tableName+'.id'
}
},
related_drops_animations: {
relation: this.HasOneRelation,
modelClass: DropsAnimationsModel,
join: {
from: this.tableName+'.id',
to: DropsAnimationsModel.tableName+'.item_id'
}
},
related_items_inventory: {
relation: this.HasManyRelation,
modelClass: ItemsInventoryModel,
join: {
from: this.tableName+'.id',
to: ItemsInventoryModel.tableName+'.item_id'
}
},
related_items_item_modifiers: {
relation: this.HasManyRelation,
modelClass: ItemsItemModifiersModel,
join: {
from: this.tableName+'.id',
to: ItemsItemModifiersModel.tableName+'.item_id'
}
},
related_objects_items_inventory: {
relation: this.HasManyRelation,
modelClass: ObjectsItemsInventoryModel,
join: {
from: this.tableName+'.id',
to: ObjectsItemsInventoryModel.tableName+'.item_id'
}
},
related_objects_items_requirements_item_key: {
relation: this.HasManyRelation,
modelClass: ObjectsItemsRequirementsModel,
join: {
from: this.tableName+'.key',
to: ObjectsItemsRequirementsModel.tableName+'.item_key'
}
},
related_objects_items_requirements_required_item_key: {
relation: this.HasManyRelation,
modelClass: ObjectsItemsRequirementsModel,
join: {
from: this.tableName+'.key',
to: ObjectsItemsRequirementsModel.tableName+'.required_item_key'
}
},
related_objects_items_rewards_item_key: {
relation: this.HasManyRelation,
modelClass: ObjectsItemsRewardsModel,
join: {
from: this.tableName+'.key',
to: ObjectsItemsRewardsModel.tableName+'.item_key'
}
},
related_objects_items_rewards_reward_item_key: {
relation: this.HasManyRelation,
modelClass: ObjectsItemsRewardsModel,
join: {
from: this.tableName+'.key',
to: ObjectsItemsRewardsModel.tableName+'.reward_item_key'
}
},
related_rewards: {
relation: this.HasManyRelation,
modelClass: RewardsModel,
join: {
from: this.tableName+'.id',
to: RewardsModel.tableName+'.item_id'
}
}
};
}
}
module.exports.ItemsItemModel = ItemsItemModel;
================================================
FILE: generated-entities/models/objection-js/items-item-modifiers-model.js
================================================
/**
*
* Reldens - ItemsItemModifiersModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ItemsItemModifiersModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'items_item_modifiers';
}
static get relationMappings()
{
const { ItemsItemModel } = require('./items-item-model');
const { OperationTypesModel } = require('./operation-types-model');
return {
related_items_item: {
relation: this.BelongsToOneRelation,
modelClass: ItemsItemModel,
join: {
from: this.tableName+'.item_id',
to: ItemsItemModel.tableName+'.id'
}
},
related_operation_types: {
relation: this.BelongsToOneRelation,
modelClass: OperationTypesModel,
join: {
from: this.tableName+'.operation',
to: OperationTypesModel.tableName+'.id'
}
}
};
}
}
module.exports.ItemsItemModifiersModel = ItemsItemModifiersModel;
================================================
FILE: generated-entities/models/objection-js/items-types-model.js
================================================
/**
*
* Reldens - ItemsTypesModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ItemsTypesModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'items_types';
}
static get relationMappings()
{
const { ItemsItemModel } = require('./items-item-model');
return {
related_items_item: {
relation: this.HasManyRelation,
modelClass: ItemsItemModel,
join: {
from: this.tableName+'.id',
to: ItemsItemModel.tableName+'.type'
}
}
};
}
}
module.exports.ItemsTypesModel = ItemsTypesModel;
================================================
FILE: generated-entities/models/objection-js/locale-model.js
================================================
/**
*
* Reldens - LocaleModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class LocaleModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'locale';
}
static get relationMappings()
{
const { SnippetsModel } = require('./snippets-model');
const { UsersLocaleModel } = require('./users-locale-model');
return {
related_snippets: {
relation: this.HasManyRelation,
modelClass: SnippetsModel,
join: {
from: this.tableName+'.id',
to: SnippetsModel.tableName+'.locale_id'
}
},
related_users_locale: {
relation: this.HasManyRelation,
modelClass: UsersLocaleModel,
join: {
from: this.tableName+'.id',
to: UsersLocaleModel.tableName+'.locale_id'
}
}
};
}
}
module.exports.LocaleModel = LocaleModel;
================================================
FILE: generated-entities/models/objection-js/objects-animations-model.js
================================================
/**
*
* Reldens - ObjectsAnimationsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ObjectsAnimationsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'objects_animations';
}
static get relationMappings()
{
const { ObjectsModel } = require('./objects-model');
return {
related_objects: {
relation: this.BelongsToOneRelation,
modelClass: ObjectsModel,
join: {
from: this.tableName+'.object_id',
to: ObjectsModel.tableName+'.id'
}
}
};
}
}
module.exports.ObjectsAnimationsModel = ObjectsAnimationsModel;
================================================
FILE: generated-entities/models/objection-js/objects-assets-model.js
================================================
/**
*
* Reldens - ObjectsAssetsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ObjectsAssetsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'objects_assets';
}
static get idColumn()
{
return 'object_asset_id';
}
static get relationMappings()
{
const { ObjectsModel } = require('./objects-model');
return {
related_objects: {
relation: this.BelongsToOneRelation,
modelClass: ObjectsModel,
join: {
from: this.tableName+'.object_id',
to: ObjectsModel.tableName+'.id'
}
}
};
}
}
module.exports.ObjectsAssetsModel = ObjectsAssetsModel;
================================================
FILE: generated-entities/models/objection-js/objects-items-inventory-model.js
================================================
/**
*
* Reldens - ObjectsItemsInventoryModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ObjectsItemsInventoryModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'objects_items_inventory';
}
static get relationMappings()
{
const { ObjectsModel } = require('./objects-model');
const { ItemsItemModel } = require('./items-item-model');
return {
related_objects: {
relation: this.BelongsToOneRelation,
modelClass: ObjectsModel,
join: {
from: this.tableName+'.owner_id',
to: ObjectsModel.tableName+'.id'
}
},
related_items_item: {
relation: this.BelongsToOneRelation,
modelClass: ItemsItemModel,
join: {
from: this.tableName+'.item_id',
to: ItemsItemModel.tableName+'.id'
}
}
};
}
}
module.exports.ObjectsItemsInventoryModel = ObjectsItemsInventoryModel;
================================================
FILE: generated-entities/models/objection-js/objects-items-requirements-model.js
================================================
/**
*
* Reldens - ObjectsItemsRequirementsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ObjectsItemsRequirementsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'objects_items_requirements';
}
static get relationMappings()
{
const { ObjectsModel } = require('./objects-model');
const { ItemsItemModel } = require('./items-item-model');
return {
related_objects: {
relation: this.BelongsToOneRelation,
modelClass: ObjectsModel,
join: {
from: this.tableName+'.object_id',
to: ObjectsModel.tableName+'.id'
}
},
related_items_item_item_key: {
relation: this.BelongsToOneRelation,
modelClass: ItemsItemModel,
join: {
from: this.tableName+'.item_key',
to: ItemsItemModel.tableName+'.key'
}
},
related_items_item_required_item_key: {
relation: this.BelongsToOneRelation,
modelClass: ItemsItemModel,
join: {
from: this.tableName+'.required_item_key',
to: ItemsItemModel.tableName+'.key'
}
}
};
}
}
module.exports.ObjectsItemsRequirementsModel = ObjectsItemsRequirementsModel;
================================================
FILE: generated-entities/models/objection-js/objects-items-rewards-model.js
================================================
/**
*
* Reldens - ObjectsItemsRewardsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ObjectsItemsRewardsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'objects_items_rewards';
}
static get relationMappings()
{
const { ObjectsModel } = require('./objects-model');
const { ItemsItemModel } = require('./items-item-model');
return {
related_objects: {
relation: this.BelongsToOneRelation,
modelClass: ObjectsModel,
join: {
from: this.tableName+'.object_id',
to: ObjectsModel.tableName+'.id'
}
},
related_items_item_item_key: {
relation: this.BelongsToOneRelation,
modelClass: ItemsItemModel,
join: {
from: this.tableName+'.item_key',
to: ItemsItemModel.tableName+'.key'
}
},
related_items_item_reward_item_key: {
relation: this.BelongsToOneRelation,
modelClass: ItemsItemModel,
join: {
from: this.tableName+'.reward_item_key',
to: ItemsItemModel.tableName+'.key'
}
}
};
}
}
module.exports.ObjectsItemsRewardsModel = ObjectsItemsRewardsModel;
================================================
FILE: generated-entities/models/objection-js/objects-model.js
================================================
/**
*
* Reldens - ObjectsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ObjectsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'objects';
}
static get relationMappings()
{
const { RoomsModel } = require('./rooms-model');
const { ObjectsTypesModel } = require('./objects-types-model');
const { ObjectsAnimationsModel } = require('./objects-animations-model');
const { ObjectsAssetsModel } = require('./objects-assets-model');
const { ObjectsItemsInventoryModel } = require('./objects-items-inventory-model');
const { ObjectsItemsRequirementsModel } = require('./objects-items-requirements-model');
const { ObjectsItemsRewardsModel } = require('./objects-items-rewards-model');
const { ObjectsSkillsModel } = require('./objects-skills-model');
const { ObjectsStatsModel } = require('./objects-stats-model');
const { RespawnModel } = require('./respawn-model');
const { RewardsModel } = require('./rewards-model');
return {
related_rooms: {
relation: this.BelongsToOneRelation,
modelClass: RoomsModel,
join: {
from: this.tableName+'.room_id',
to: RoomsModel.tableName+'.id'
}
},
related_objects_types: {
relation: this.BelongsToOneRelation,
modelClass: ObjectsTypesModel,
join: {
from: this.tableName+'.class_type',
to: ObjectsTypesModel.tableName+'.id'
}
},
related_objects_animations: {
relation: this.HasManyRelation,
modelClass: ObjectsAnimationsModel,
join: {
from: this.tableName+'.id',
to: ObjectsAnimationsModel.tableName+'.object_id'
}
},
related_objects_assets: {
relation: this.HasManyRelation,
modelClass: ObjectsAssetsModel,
join: {
from: this.tableName+'.id',
to: ObjectsAssetsModel.tableName+'.object_id'
}
},
related_objects_items_inventory: {
relation: this.HasManyRelation,
modelClass: ObjectsItemsInventoryModel,
join: {
from: this.tableName+'.id',
to: ObjectsItemsInventoryModel.tableName+'.owner_id'
}
},
related_objects_items_requirements: {
relation: this.HasManyRelation,
modelClass: ObjectsItemsRequirementsModel,
join: {
from: this.tableName+'.id',
to: ObjectsItemsRequirementsModel.tableName+'.object_id'
}
},
related_objects_items_rewards: {
relation: this.HasManyRelation,
modelClass: ObjectsItemsRewardsModel,
join: {
from: this.tableName+'.id',
to: ObjectsItemsRewardsModel.tableName+'.object_id'
}
},
related_objects_skills: {
relation: this.HasManyRelation,
modelClass: ObjectsSkillsModel,
join: {
from: this.tableName+'.id',
to: ObjectsSkillsModel.tableName+'.object_id'
}
},
related_objects_stats: {
relation: this.HasManyRelation,
modelClass: ObjectsStatsModel,
join: {
from: this.tableName+'.id',
to: ObjectsStatsModel.tableName+'.object_id'
}
},
related_respawn: {
relation: this.HasManyRelation,
modelClass: RespawnModel,
join: {
from: this.tableName+'.id',
to: RespawnModel.tableName+'.object_id'
}
},
related_rewards: {
relation: this.HasManyRelation,
modelClass: RewardsModel,
join: {
from: this.tableName+'.id',
to: RewardsModel.tableName+'.object_id'
}
}
};
}
}
module.exports.ObjectsModel = ObjectsModel;
================================================
FILE: generated-entities/models/objection-js/objects-skills-model.js
================================================
/**
*
* Reldens - ObjectsSkillsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ObjectsSkillsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'objects_skills';
}
static get relationMappings()
{
const { ObjectsModel } = require('./objects-model');
const { SkillsSkillModel } = require('./skills-skill-model');
const { TargetOptionsModel } = require('./target-options-model');
return {
related_objects: {
relation: this.BelongsToOneRelation,
modelClass: ObjectsModel,
join: {
from: this.tableName+'.object_id',
to: ObjectsModel.tableName+'.id'
}
},
related_skills_skill: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillModel,
join: {
from: this.tableName+'.skill_id',
to: SkillsSkillModel.tableName+'.id'
}
},
related_target_options: {
relation: this.BelongsToOneRelation,
modelClass: TargetOptionsModel,
join: {
from: this.tableName+'.target_id',
to: TargetOptionsModel.tableName+'.id'
}
}
};
}
}
module.exports.ObjectsSkillsModel = ObjectsSkillsModel;
================================================
FILE: generated-entities/models/objection-js/objects-stats-model.js
================================================
/**
*
* Reldens - ObjectsStatsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ObjectsStatsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'objects_stats';
}
static get relationMappings()
{
const { ObjectsModel } = require('./objects-model');
const { StatsModel } = require('./stats-model');
return {
related_objects: {
relation: this.BelongsToOneRelation,
modelClass: ObjectsModel,
join: {
from: this.tableName+'.object_id',
to: ObjectsModel.tableName+'.id'
}
},
related_stats: {
relation: this.BelongsToOneRelation,
modelClass: StatsModel,
join: {
from: this.tableName+'.stat_id',
to: StatsModel.tableName+'.id'
}
}
};
}
}
module.exports.ObjectsStatsModel = ObjectsStatsModel;
================================================
FILE: generated-entities/models/objection-js/objects-types-model.js
================================================
/**
*
* Reldens - ObjectsTypesModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ObjectsTypesModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'objects_types';
}
static get relationMappings()
{
const { ObjectsModel } = require('./objects-model');
return {
related_objects: {
relation: this.HasManyRelation,
modelClass: ObjectsModel,
join: {
from: this.tableName+'.id',
to: ObjectsModel.tableName+'.class_type'
}
}
};
}
}
module.exports.ObjectsTypesModel = ObjectsTypesModel;
================================================
FILE: generated-entities/models/objection-js/operation-types-model.js
================================================
/**
*
* Reldens - OperationTypesModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class OperationTypesModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'operation_types';
}
static get relationMappings()
{
const { ClanLevelsModifiersModel } = require('./clan-levels-modifiers-model');
const { ItemsItemModifiersModel } = require('./items-item-modifiers-model');
const { RewardsModifiersModel } = require('./rewards-modifiers-model');
const { SkillsLevelsModifiersModel } = require('./skills-levels-modifiers-model');
const { SkillsSkillOwnerEffectsModel } = require('./skills-skill-owner-effects-model');
const { SkillsSkillTargetEffectsModel } = require('./skills-skill-target-effects-model');
return {
related_clan_levels_modifiers: {
relation: this.HasManyRelation,
modelClass: ClanLevelsModifiersModel,
join: {
from: this.tableName+'.key',
to: ClanLevelsModifiersModel.tableName+'.operation'
}
},
related_items_item_modifiers: {
relation: this.HasManyRelation,
modelClass: ItemsItemModifiersModel,
join: {
from: this.tableName+'.id',
to: ItemsItemModifiersModel.tableName+'.operation'
}
},
related_rewards_modifiers: {
relation: this.HasManyRelation,
modelClass: RewardsModifiersModel,
join: {
from: this.tableName+'.id',
to: RewardsModifiersModel.tableName+'.operation'
}
},
related_skills_levels_modifiers: {
relation: this.HasManyRelation,
modelClass: SkillsLevelsModifiersModel,
join: {
from: this.tableName+'.key',
to: SkillsLevelsModifiersModel.tableName+'.operation'
}
},
related_skills_skill_owner_effects: {
relation: this.HasManyRelation,
modelClass: SkillsSkillOwnerEffectsModel,
join: {
from: this.tableName+'.key',
to: SkillsSkillOwnerEffectsModel.tableName+'.operation'
}
},
related_skills_skill_target_effects: {
relation: this.HasManyRelation,
modelClass: SkillsSkillTargetEffectsModel,
join: {
from: this.tableName+'.key',
to: SkillsSkillTargetEffectsModel.tableName+'.operation'
}
}
};
}
}
module.exports.OperationTypesModel = OperationTypesModel;
================================================
FILE: generated-entities/models/objection-js/players-model.js
================================================
/**
*
* Reldens - PlayersModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class PlayersModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'players';
}
static get relationMappings()
{
const { UsersModel } = require('./users-model');
const { AdsPlayedModel } = require('./ads-played-model');
const { AudioPlayerConfigModel } = require('./audio-player-config-model');
const { ChatModel } = require('./chat-model');
const { ClanModel } = require('./clan-model');
const { ClanMembersModel } = require('./clan-members-model');
const { ItemsInventoryModel } = require('./items-inventory-model');
const { PlayersStateModel } = require('./players-state-model');
const { PlayersStatsModel } = require('./players-stats-model');
const { RewardsEventsStateModel } = require('./rewards-events-state-model');
const { ScoresModel } = require('./scores-model');
const { ScoresDetailModel } = require('./scores-detail-model');
const { SkillsOwnersClassPathModel } = require('./skills-owners-class-path-model');
return {
related_users: {
relation: this.BelongsToOneRelation,
modelClass: UsersModel,
join: {
from: this.tableName+'.user_id',
to: UsersModel.tableName+'.id'
}
},
related_ads_played: {
relation: this.HasManyRelation,
modelClass: AdsPlayedModel,
join: {
from: this.tableName+'.id',
to: AdsPlayedModel.tableName+'.player_id'
}
},
related_audio_player_config: {
relation: this.HasManyRelation,
modelClass: AudioPlayerConfigModel,
join: {
from: this.tableName+'.id',
to: AudioPlayerConfigModel.tableName+'.player_id'
}
},
related_chat_player: {
relation: this.HasManyRelation,
modelClass: ChatModel,
join: {
from: this.tableName+'.id',
to: ChatModel.tableName+'.player_id'
}
},
related_chat_private_player: {
relation: this.HasManyRelation,
modelClass: ChatModel,
join: {
from: this.tableName+'.id',
to: ChatModel.tableName+'.private_player_id'
}
},
related_clan: {
relation: this.HasOneRelation,
modelClass: ClanModel,
join: {
from: this.tableName+'.id',
to: ClanModel.tableName+'.owner_id'
}
},
related_clan_members: {
relation: this.HasOneRelation,
modelClass: ClanMembersModel,
join: {
from: this.tableName+'.id',
to: ClanMembersModel.tableName+'.player_id'
}
},
related_items_inventory: {
relation: this.HasManyRelation,
modelClass: ItemsInventoryModel,
join: {
from: this.tableName+'.id',
to: ItemsInventoryModel.tableName+'.owner_id'
}
},
related_players_state: {
relation: this.HasOneRelation,
modelClass: PlayersStateModel,
join: {
from: this.tableName+'.id',
to: PlayersStateModel.tableName+'.player_id'
}
},
related_players_stats: {
relation: this.HasManyRelation,
modelClass: PlayersStatsModel,
join: {
from: this.tableName+'.id',
to: PlayersStatsModel.tableName+'.player_id'
}
},
related_rewards_events_state: {
relation: this.HasManyRelation,
modelClass: RewardsEventsStateModel,
join: {
from: this.tableName+'.id',
to: RewardsEventsStateModel.tableName+'.player_id'
}
},
related_scores: {
relation: this.HasManyRelation,
modelClass: ScoresModel,
join: {
from: this.tableName+'.id',
to: ScoresModel.tableName+'.player_id'
}
},
related_scores_detail: {
relation: this.HasManyRelation,
modelClass: ScoresDetailModel,
join: {
from: this.tableName+'.id',
to: ScoresDetailModel.tableName+'.player_id'
}
},
related_skills_owners_class_path: {
relation: this.HasManyRelation,
modelClass: SkillsOwnersClassPathModel,
join: {
from: this.tableName+'.id',
to: SkillsOwnersClassPathModel.tableName+'.owner_id'
}
}
};
}
}
module.exports.PlayersModel = PlayersModel;
================================================
FILE: generated-entities/models/objection-js/players-state-model.js
================================================
/**
*
* Reldens - PlayersStateModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class PlayersStateModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'players_state';
}
static get relationMappings()
{
const { PlayersModel } = require('./players-model');
const { RoomsModel } = require('./rooms-model');
return {
related_players: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.player_id',
to: PlayersModel.tableName+'.id'
}
},
related_rooms: {
relation: this.BelongsToOneRelation,
modelClass: RoomsModel,
join: {
from: this.tableName+'.room_id',
to: RoomsModel.tableName+'.id'
}
}
};
}
}
module.exports.PlayersStateModel = PlayersStateModel;
================================================
FILE: generated-entities/models/objection-js/players-stats-model.js
================================================
/**
*
* Reldens - PlayersStatsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class PlayersStatsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'players_stats';
}
static get relationMappings()
{
const { PlayersModel } = require('./players-model');
const { StatsModel } = require('./stats-model');
return {
related_players: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.player_id',
to: PlayersModel.tableName+'.id'
}
},
related_stats: {
relation: this.BelongsToOneRelation,
modelClass: StatsModel,
join: {
from: this.tableName+'.stat_id',
to: StatsModel.tableName+'.id'
}
}
};
}
}
module.exports.PlayersStatsModel = PlayersStatsModel;
================================================
FILE: generated-entities/models/objection-js/registered-models-objection-js.js
================================================
/**
*
* Reldens - Registered Models
*
*/
const { AdsBannerModel } = require('./ads-banner-model');
const { AdsModel } = require('./ads-model');
const { AdsEventVideoModel } = require('./ads-event-video-model');
const { AdsPlayedModel } = require('./ads-played-model');
const { AdsProvidersModel } = require('./ads-providers-model');
const { AdsTypesModel } = require('./ads-types-model');
const { AudioCategoriesModel } = require('./audio-categories-model');
const { AudioModel } = require('./audio-model');
const { AudioMarkersModel } = require('./audio-markers-model');
const { AudioPlayerConfigModel } = require('./audio-player-config-model');
const { ChatModel } = require('./chat-model');
const { ChatMessageTypesModel } = require('./chat-message-types-model');
const { ClanModel } = require('./clan-model');
const { ClanLevelsModel } = require('./clan-levels-model');
const { ClanLevelsModifiersModel } = require('./clan-levels-modifiers-model');
const { ClanMembersModel } = require('./clan-members-model');
const { ConfigModel } = require('./config-model');
const { ConfigTypesModel } = require('./config-types-model');
const { DropsAnimationsModel } = require('./drops-animations-model');
const { FeaturesModel } = require('./features-model');
const { ItemsGroupModel } = require('./items-group-model');
const { ItemsInventoryModel } = require('./items-inventory-model');
const { ItemsItemModel } = require('./items-item-model');
const { ItemsItemModifiersModel } = require('./items-item-modifiers-model');
const { ItemsTypesModel } = require('./items-types-model');
const { LocaleModel } = require('./locale-model');
const { ObjectsAnimationsModel } = require('./objects-animations-model');
const { ObjectsAssetsModel } = require('./objects-assets-model');
const { ObjectsModel } = require('./objects-model');
const { ObjectsItemsInventoryModel } = require('./objects-items-inventory-model');
const { ObjectsItemsRequirementsModel } = require('./objects-items-requirements-model');
const { ObjectsItemsRewardsModel } = require('./objects-items-rewards-model');
const { ObjectsSkillsModel } = require('./objects-skills-model');
const { ObjectsStatsModel } = require('./objects-stats-model');
const { ObjectsTypesModel } = require('./objects-types-model');
const { OperationTypesModel } = require('./operation-types-model');
const { PlayersModel } = require('./players-model');
const { PlayersStateModel } = require('./players-state-model');
const { PlayersStatsModel } = require('./players-stats-model');
const { RespawnModel } = require('./respawn-model');
const { RewardsModel } = require('./rewards-model');
const { RewardsEventsModel } = require('./rewards-events-model');
const { RewardsEventsStateModel } = require('./rewards-events-state-model');
const { RewardsModifiersModel } = require('./rewards-modifiers-model');
const { RoomsChangePointsModel } = require('./rooms-change-points-model');
const { RoomsModel } = require('./rooms-model');
const { RoomsReturnPointsModel } = require('./rooms-return-points-model');
const { ScoresDetailModel } = require('./scores-detail-model');
const { ScoresModel } = require('./scores-model');
const { SkillsClassLevelUpAnimationsModel } = require('./skills-class-level-up-animations-model');
const { SkillsClassPathModel } = require('./skills-class-path-model');
const { SkillsClassPathLevelLabelsModel } = require('./skills-class-path-level-labels-model');
const { SkillsClassPathLevelSkillsModel } = require('./skills-class-path-level-skills-model');
const { SkillsGroupsModel } = require('./skills-groups-model');
const { SkillsLevelsModel } = require('./skills-levels-model');
const { SkillsLevelsModifiersConditionsModel } = require('./skills-levels-modifiers-conditions-model');
const { SkillsLevelsModifiersModel } = require('./skills-levels-modifiers-model');
const { SkillsLevelsSetModel } = require('./skills-levels-set-model');
const { SkillsOwnersClassPathModel } = require('./skills-owners-class-path-model');
const { SkillsSkillAnimationsModel } = require('./skills-skill-animations-model');
const { SkillsSkillAttackModel } = require('./skills-skill-attack-model');
const { SkillsSkillModel } = require('./skills-skill-model');
const { SkillsSkillGroupRelationModel } = require('./skills-skill-group-relation-model');
const { SkillsSkillOwnerConditionsModel } = require('./skills-skill-owner-conditions-model');
const { SkillsSkillOwnerEffectsConditionsModel } = require('./skills-skill-owner-effects-conditions-model');
const { SkillsSkillOwnerEffectsModel } = require('./skills-skill-owner-effects-model');
const { SkillsSkillPhysicalDataModel } = require('./skills-skill-physical-data-model');
const { SkillsSkillTargetEffectsConditionsModel } = require('./skills-skill-target-effects-conditions-model');
const { SkillsSkillTargetEffectsModel } = require('./skills-skill-target-effects-model');
const { SkillsSkillTypeModel } = require('./skills-skill-type-model');
const { SnippetsModel } = require('./snippets-model');
const { StatsModel } = require('./stats-model');
const { TargetOptionsModel } = require('./target-options-model');
const { UsersModel } = require('./users-model');
const { UsersLocaleModel } = require('./users-locale-model');
const { UsersLoginModel } = require('./users-login-model');
const { entitiesConfig } = require('../../entities-config');
const { entitiesTranslations } = require('../../entities-translations');
let rawRegisteredEntities = {
adsBanner: AdsBannerModel,
ads: AdsModel,
adsEventVideo: AdsEventVideoModel,
adsPlayed: AdsPlayedModel,
adsProviders: AdsProvidersModel,
adsTypes: AdsTypesModel,
audioCategories: AudioCategoriesModel,
audio: AudioModel,
audioMarkers: AudioMarkersModel,
audioPlayerConfig: AudioPlayerConfigModel,
chat: ChatModel,
chatMessageTypes: ChatMessageTypesModel,
clan: ClanModel,
clanLevels: ClanLevelsModel,
clanLevelsModifiers: ClanLevelsModifiersModel,
clanMembers: ClanMembersModel,
config: ConfigModel,
configTypes: ConfigTypesModel,
dropsAnimations: DropsAnimationsModel,
features: FeaturesModel,
itemsGroup: ItemsGroupModel,
itemsInventory: ItemsInventoryModel,
itemsItem: ItemsItemModel,
itemsItemModifiers: ItemsItemModifiersModel,
itemsTypes: ItemsTypesModel,
locale: LocaleModel,
objectsAnimations: ObjectsAnimationsModel,
objectsAssets: ObjectsAssetsModel,
objects: ObjectsModel,
objectsItemsInventory: ObjectsItemsInventoryModel,
objectsItemsRequirements: ObjectsItemsRequirementsModel,
objectsItemsRewards: ObjectsItemsRewardsModel,
objectsSkills: ObjectsSkillsModel,
objectsStats: ObjectsStatsModel,
objectsTypes: ObjectsTypesModel,
operationTypes: OperationTypesModel,
players: PlayersModel,
playersState: PlayersStateModel,
playersStats: PlayersStatsModel,
respawn: RespawnModel,
rewards: RewardsModel,
rewardsEvents: RewardsEventsModel,
rewardsEventsState: RewardsEventsStateModel,
rewardsModifiers: RewardsModifiersModel,
roomsChangePoints: RoomsChangePointsModel,
rooms: RoomsModel,
roomsReturnPoints: RoomsReturnPointsModel,
scoresDetail: ScoresDetailModel,
scores: ScoresModel,
skillsClassLevelUpAnimations: SkillsClassLevelUpAnimationsModel,
skillsClassPath: SkillsClassPathModel,
skillsClassPathLevelLabels: SkillsClassPathLevelLabelsModel,
skillsClassPathLevelSkills: SkillsClassPathLevelSkillsModel,
skillsGroups: SkillsGroupsModel,
skillsLevels: SkillsLevelsModel,
skillsLevelsModifiersConditions: SkillsLevelsModifiersConditionsModel,
skillsLevelsModifiers: SkillsLevelsModifiersModel,
skillsLevelsSet: SkillsLevelsSetModel,
skillsOwnersClassPath: SkillsOwnersClassPathModel,
skillsSkillAnimations: SkillsSkillAnimationsModel,
skillsSkillAttack: SkillsSkillAttackModel,
skillsSkill: SkillsSkillModel,
skillsSkillGroupRelation: SkillsSkillGroupRelationModel,
skillsSkillOwnerConditions: SkillsSkillOwnerConditionsModel,
skillsSkillOwnerEffectsConditions: SkillsSkillOwnerEffectsConditionsModel,
skillsSkillOwnerEffects: SkillsSkillOwnerEffectsModel,
skillsSkillPhysicalData: SkillsSkillPhysicalDataModel,
skillsSkillTargetEffectsConditions: SkillsSkillTargetEffectsConditionsModel,
skillsSkillTargetEffects: SkillsSkillTargetEffectsModel,
skillsSkillType: SkillsSkillTypeModel,
snippets: SnippetsModel,
stats: StatsModel,
targetOptions: TargetOptionsModel,
users: UsersModel,
usersLocale: UsersLocaleModel,
usersLogin: UsersLoginModel
};
module.exports.rawRegisteredEntities = rawRegisteredEntities;
module.exports.entitiesConfig = entitiesConfig;
module.exports.entitiesTranslations = entitiesTranslations;
================================================
FILE: generated-entities/models/objection-js/respawn-model.js
================================================
/**
*
* Reldens - RespawnModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class RespawnModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'respawn';
}
static get relationMappings()
{
const { ObjectsModel } = require('./objects-model');
return {
related_objects: {
relation: this.BelongsToOneRelation,
modelClass: ObjectsModel,
join: {
from: this.tableName+'.object_id',
to: ObjectsModel.tableName+'.id'
}
}
};
}
}
module.exports.RespawnModel = RespawnModel;
================================================
FILE: generated-entities/models/objection-js/rewards-events-model.js
================================================
/**
*
* Reldens - RewardsEventsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class RewardsEventsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'rewards_events';
}
static get relationMappings()
{
const { RewardsEventsStateModel } = require('./rewards-events-state-model');
return {
related_rewards_events_state: {
relation: this.HasManyRelation,
modelClass: RewardsEventsStateModel,
join: {
from: this.tableName+'.id',
to: RewardsEventsStateModel.tableName+'.rewards_events_id'
}
}
};
}
}
module.exports.RewardsEventsModel = RewardsEventsModel;
================================================
FILE: generated-entities/models/objection-js/rewards-events-state-model.js
================================================
/**
*
* Reldens - RewardsEventsStateModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class RewardsEventsStateModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'rewards_events_state';
}
static get relationMappings()
{
const { RewardsEventsModel } = require('./rewards-events-model');
const { PlayersModel } = require('./players-model');
return {
related_rewards_events: {
relation: this.BelongsToOneRelation,
modelClass: RewardsEventsModel,
join: {
from: this.tableName+'.rewards_events_id',
to: RewardsEventsModel.tableName+'.id'
}
},
related_players: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.player_id',
to: PlayersModel.tableName+'.id'
}
}
};
}
}
module.exports.RewardsEventsStateModel = RewardsEventsStateModel;
================================================
FILE: generated-entities/models/objection-js/rewards-model.js
================================================
/**
*
* Reldens - RewardsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class RewardsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'rewards';
}
static get relationMappings()
{
const { ObjectsModel } = require('./objects-model');
const { ItemsItemModel } = require('./items-item-model');
const { RewardsModifiersModel } = require('./rewards-modifiers-model');
return {
related_objects: {
relation: this.BelongsToOneRelation,
modelClass: ObjectsModel,
join: {
from: this.tableName+'.object_id',
to: ObjectsModel.tableName+'.id'
}
},
related_items_item: {
relation: this.BelongsToOneRelation,
modelClass: ItemsItemModel,
join: {
from: this.tableName+'.item_id',
to: ItemsItemModel.tableName+'.id'
}
},
related_rewards_modifiers: {
relation: this.BelongsToOneRelation,
modelClass: RewardsModifiersModel,
join: {
from: this.tableName+'.modifier_id',
to: RewardsModifiersModel.tableName+'.id'
}
}
};
}
}
module.exports.RewardsModel = RewardsModel;
================================================
FILE: generated-entities/models/objection-js/rewards-modifiers-model.js
================================================
/**
*
* Reldens - RewardsModifiersModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class RewardsModifiersModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'rewards_modifiers';
}
static get relationMappings()
{
const { OperationTypesModel } = require('./operation-types-model');
const { RewardsModel } = require('./rewards-model');
return {
related_operation_types: {
relation: this.BelongsToOneRelation,
modelClass: OperationTypesModel,
join: {
from: this.tableName+'.operation',
to: OperationTypesModel.tableName+'.id'
}
},
related_rewards: {
relation: this.HasManyRelation,
modelClass: RewardsModel,
join: {
from: this.tableName+'.id',
to: RewardsModel.tableName+'.modifier_id'
}
}
};
}
}
module.exports.RewardsModifiersModel = RewardsModifiersModel;
================================================
FILE: generated-entities/models/objection-js/rooms-change-points-model.js
================================================
/**
*
* Reldens - RoomsChangePointsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class RoomsChangePointsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'rooms_change_points';
}
static get relationMappings()
{
const { RoomsModel } = require('./rooms-model');
return {
related_rooms_room: {
relation: this.BelongsToOneRelation,
modelClass: RoomsModel,
join: {
from: this.tableName+'.room_id',
to: RoomsModel.tableName+'.id'
}
},
related_rooms_next_room: {
relation: this.BelongsToOneRelation,
modelClass: RoomsModel,
join: {
from: this.tableName+'.next_room_id',
to: RoomsModel.tableName+'.id'
}
}
};
}
}
module.exports.RoomsChangePointsModel = RoomsChangePointsModel;
================================================
FILE: generated-entities/models/objection-js/rooms-model.js
================================================
/**
*
* Reldens - RoomsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class RoomsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'rooms';
}
static get relationMappings()
{
const { AudioModel } = require('./audio-model');
const { ChatModel } = require('./chat-model');
const { ObjectsModel } = require('./objects-model');
const { PlayersStateModel } = require('./players-state-model');
const { RoomsChangePointsModel } = require('./rooms-change-points-model');
const { RoomsReturnPointsModel } = require('./rooms-return-points-model');
return {
related_audio: {
relation: this.HasManyRelation,
modelClass: AudioModel,
join: {
from: this.tableName+'.id',
to: AudioModel.tableName+'.room_id'
}
},
related_chat: {
relation: this.HasManyRelation,
modelClass: ChatModel,
join: {
from: this.tableName+'.id',
to: ChatModel.tableName+'.room_id'
}
},
related_objects: {
relation: this.HasManyRelation,
modelClass: ObjectsModel,
join: {
from: this.tableName+'.id',
to: ObjectsModel.tableName+'.room_id'
}
},
related_players_state: {
relation: this.HasManyRelation,
modelClass: PlayersStateModel,
join: {
from: this.tableName+'.id',
to: PlayersStateModel.tableName+'.room_id'
}
},
related_rooms_change_points_room: {
relation: this.HasManyRelation,
modelClass: RoomsChangePointsModel,
join: {
from: this.tableName+'.id',
to: RoomsChangePointsModel.tableName+'.room_id'
}
},
related_rooms_change_points_next_room: {
relation: this.HasManyRelation,
modelClass: RoomsChangePointsModel,
join: {
from: this.tableName+'.id',
to: RoomsChangePointsModel.tableName+'.next_room_id'
}
},
related_rooms_return_points_room: {
relation: this.HasManyRelation,
modelClass: RoomsReturnPointsModel,
join: {
from: this.tableName+'.id',
to: RoomsReturnPointsModel.tableName+'.room_id'
}
},
related_rooms_return_points_from_room: {
relation: this.HasManyRelation,
modelClass: RoomsReturnPointsModel,
join: {
from: this.tableName+'.id',
to: RoomsReturnPointsModel.tableName+'.from_room_id'
}
}
};
}
}
module.exports.RoomsModel = RoomsModel;
================================================
FILE: generated-entities/models/objection-js/rooms-return-points-model.js
================================================
/**
*
* Reldens - RoomsReturnPointsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class RoomsReturnPointsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'rooms_return_points';
}
static get relationMappings()
{
const { RoomsModel } = require('./rooms-model');
return {
related_rooms_room: {
relation: this.BelongsToOneRelation,
modelClass: RoomsModel,
join: {
from: this.tableName+'.room_id',
to: RoomsModel.tableName+'.id'
}
},
related_rooms_from_room: {
relation: this.BelongsToOneRelation,
modelClass: RoomsModel,
join: {
from: this.tableName+'.from_room_id',
to: RoomsModel.tableName+'.id'
}
}
};
}
}
module.exports.RoomsReturnPointsModel = RoomsReturnPointsModel;
================================================
FILE: generated-entities/models/objection-js/scores-detail-model.js
================================================
/**
*
* Reldens - ScoresDetailModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ScoresDetailModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'scores_detail';
}
static get relationMappings()
{
const { PlayersModel } = require('./players-model');
return {
related_players: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.player_id',
to: PlayersModel.tableName+'.id'
}
}
};
}
}
module.exports.ScoresDetailModel = ScoresDetailModel;
================================================
FILE: generated-entities/models/objection-js/scores-model.js
================================================
/**
*
* Reldens - ScoresModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class ScoresModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'scores';
}
static get relationMappings()
{
const { PlayersModel } = require('./players-model');
return {
related_players: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.player_id',
to: PlayersModel.tableName+'.id'
}
}
};
}
}
module.exports.ScoresModel = ScoresModel;
================================================
FILE: generated-entities/models/objection-js/skills-class-level-up-animations-model.js
================================================
/**
*
* Reldens - SkillsClassLevelUpAnimationsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsClassLevelUpAnimationsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_class_level_up_animations';
}
static get relationMappings()
{
const { SkillsClassPathModel } = require('./skills-class-path-model');
const { SkillsLevelsModel } = require('./skills-levels-model');
return {
related_skills_class_path: {
relation: this.BelongsToOneRelation,
modelClass: SkillsClassPathModel,
join: {
from: this.tableName+'.class_path_id',
to: SkillsClassPathModel.tableName+'.id'
}
},
related_skills_levels: {
relation: this.BelongsToOneRelation,
modelClass: SkillsLevelsModel,
join: {
from: this.tableName+'.level_id',
to: SkillsLevelsModel.tableName+'.id'
}
}
};
}
}
module.exports.SkillsClassLevelUpAnimationsModel = SkillsClassLevelUpAnimationsModel;
================================================
FILE: generated-entities/models/objection-js/skills-class-path-level-labels-model.js
================================================
/**
*
* Reldens - SkillsClassPathLevelLabelsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsClassPathLevelLabelsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_class_path_level_labels';
}
static get relationMappings()
{
const { SkillsClassPathModel } = require('./skills-class-path-model');
const { SkillsLevelsModel } = require('./skills-levels-model');
return {
related_skills_class_path: {
relation: this.BelongsToOneRelation,
modelClass: SkillsClassPathModel,
join: {
from: this.tableName+'.class_path_id',
to: SkillsClassPathModel.tableName+'.id'
}
},
related_skills_levels: {
relation: this.BelongsToOneRelation,
modelClass: SkillsLevelsModel,
join: {
from: this.tableName+'.level_id',
to: SkillsLevelsModel.tableName+'.id'
}
}
};
}
}
module.exports.SkillsClassPathLevelLabelsModel = SkillsClassPathLevelLabelsModel;
================================================
FILE: generated-entities/models/objection-js/skills-class-path-level-skills-model.js
================================================
/**
*
* Reldens - SkillsClassPathLevelSkillsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsClassPathLevelSkillsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_class_path_level_skills';
}
static get relationMappings()
{
const { SkillsClassPathModel } = require('./skills-class-path-model');
const { SkillsLevelsModel } = require('./skills-levels-model');
const { SkillsSkillModel } = require('./skills-skill-model');
return {
related_skills_class_path: {
relation: this.BelongsToOneRelation,
modelClass: SkillsClassPathModel,
join: {
from: this.tableName+'.class_path_id',
to: SkillsClassPathModel.tableName+'.id'
}
},
related_skills_levels: {
relation: this.BelongsToOneRelation,
modelClass: SkillsLevelsModel,
join: {
from: this.tableName+'.level_id',
to: SkillsLevelsModel.tableName+'.id'
}
},
related_skills_skill: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillModel,
join: {
from: this.tableName+'.skill_id',
to: SkillsSkillModel.tableName+'.id'
}
}
};
}
}
module.exports.SkillsClassPathLevelSkillsModel = SkillsClassPathLevelSkillsModel;
================================================
FILE: generated-entities/models/objection-js/skills-class-path-model.js
================================================
/**
*
* Reldens - SkillsClassPathModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsClassPathModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_class_path';
}
static get relationMappings()
{
const { SkillsLevelsSetModel } = require('./skills-levels-set-model');
const { SkillsClassLevelUpAnimationsModel } = require('./skills-class-level-up-animations-model');
const { SkillsClassPathLevelLabelsModel } = require('./skills-class-path-level-labels-model');
const { SkillsClassPathLevelSkillsModel } = require('./skills-class-path-level-skills-model');
const { SkillsOwnersClassPathModel } = require('./skills-owners-class-path-model');
return {
related_skills_levels_set: {
relation: this.BelongsToOneRelation,
modelClass: SkillsLevelsSetModel,
join: {
from: this.tableName+'.levels_set_id',
to: SkillsLevelsSetModel.tableName+'.id'
}
},
related_skills_class_level_up_animations: {
relation: this.HasManyRelation,
modelClass: SkillsClassLevelUpAnimationsModel,
join: {
from: this.tableName+'.id',
to: SkillsClassLevelUpAnimationsModel.tableName+'.class_path_id'
}
},
related_skills_class_path_level_labels: {
relation: this.HasManyRelation,
modelClass: SkillsClassPathLevelLabelsModel,
join: {
from: this.tableName+'.id',
to: SkillsClassPathLevelLabelsModel.tableName+'.class_path_id'
}
},
related_skills_class_path_level_skills: {
relation: this.HasManyRelation,
modelClass: SkillsClassPathLevelSkillsModel,
join: {
from: this.tableName+'.id',
to: SkillsClassPathLevelSkillsModel.tableName+'.class_path_id'
}
},
related_skills_owners_class_path: {
relation: this.HasManyRelation,
modelClass: SkillsOwnersClassPathModel,
join: {
from: this.tableName+'.id',
to: SkillsOwnersClassPathModel.tableName+'.class_path_id'
}
}
};
}
}
module.exports.SkillsClassPathModel = SkillsClassPathModel;
================================================
FILE: generated-entities/models/objection-js/skills-groups-model.js
================================================
/**
*
* Reldens - SkillsGroupsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsGroupsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_groups';
}
static get relationMappings()
{
const { SkillsSkillGroupRelationModel } = require('./skills-skill-group-relation-model');
return {
related_skills_skill_group_relation: {
relation: this.HasManyRelation,
modelClass: SkillsSkillGroupRelationModel,
join: {
from: this.tableName+'.id',
to: SkillsSkillGroupRelationModel.tableName+'.group_id'
}
}
};
}
}
module.exports.SkillsGroupsModel = SkillsGroupsModel;
================================================
FILE: generated-entities/models/objection-js/skills-levels-model.js
================================================
/**
*
* Reldens - SkillsLevelsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsLevelsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_levels';
}
static get relationMappings()
{
const { SkillsLevelsSetModel } = require('./skills-levels-set-model');
const { SkillsClassLevelUpAnimationsModel } = require('./skills-class-level-up-animations-model');
const { SkillsClassPathLevelLabelsModel } = require('./skills-class-path-level-labels-model');
const { SkillsClassPathLevelSkillsModel } = require('./skills-class-path-level-skills-model');
const { SkillsLevelsModifiersModel } = require('./skills-levels-modifiers-model');
return {
related_skills_levels_set: {
relation: this.BelongsToOneRelation,
modelClass: SkillsLevelsSetModel,
join: {
from: this.tableName+'.level_set_id',
to: SkillsLevelsSetModel.tableName+'.id'
}
},
related_skills_class_level_up_animations: {
relation: this.HasManyRelation,
modelClass: SkillsClassLevelUpAnimationsModel,
join: {
from: this.tableName+'.id',
to: SkillsClassLevelUpAnimationsModel.tableName+'.level_id'
}
},
related_skills_class_path_level_labels: {
relation: this.HasManyRelation,
modelClass: SkillsClassPathLevelLabelsModel,
join: {
from: this.tableName+'.id',
to: SkillsClassPathLevelLabelsModel.tableName+'.level_id'
}
},
related_skills_class_path_level_skills: {
relation: this.HasManyRelation,
modelClass: SkillsClassPathLevelSkillsModel,
join: {
from: this.tableName+'.id',
to: SkillsClassPathLevelSkillsModel.tableName+'.level_id'
}
},
related_skills_levels_modifiers: {
relation: this.HasManyRelation,
modelClass: SkillsLevelsModifiersModel,
join: {
from: this.tableName+'.id',
to: SkillsLevelsModifiersModel.tableName+'.level_id'
}
}
};
}
}
module.exports.SkillsLevelsModel = SkillsLevelsModel;
================================================
FILE: generated-entities/models/objection-js/skills-levels-modifiers-conditions-model.js
================================================
/**
*
* Reldens - SkillsLevelsModifiersConditionsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsLevelsModifiersConditionsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_levels_modifiers_conditions';
}
}
module.exports.SkillsLevelsModifiersConditionsModel = SkillsLevelsModifiersConditionsModel;
================================================
FILE: generated-entities/models/objection-js/skills-levels-modifiers-model.js
================================================
/**
*
* Reldens - SkillsLevelsModifiersModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsLevelsModifiersModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_levels_modifiers';
}
static get relationMappings()
{
const { SkillsLevelsModel } = require('./skills-levels-model');
const { OperationTypesModel } = require('./operation-types-model');
return {
related_skills_levels: {
relation: this.BelongsToOneRelation,
modelClass: SkillsLevelsModel,
join: {
from: this.tableName+'.level_id',
to: SkillsLevelsModel.tableName+'.id'
}
},
related_operation_types: {
relation: this.BelongsToOneRelation,
modelClass: OperationTypesModel,
join: {
from: this.tableName+'.operation',
to: OperationTypesModel.tableName+'.key'
}
}
};
}
}
module.exports.SkillsLevelsModifiersModel = SkillsLevelsModifiersModel;
================================================
FILE: generated-entities/models/objection-js/skills-levels-set-model.js
================================================
/**
*
* Reldens - SkillsLevelsSetModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsLevelsSetModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_levels_set';
}
static get relationMappings()
{
const { SkillsClassPathModel } = require('./skills-class-path-model');
const { SkillsLevelsModel } = require('./skills-levels-model');
return {
related_skills_class_path: {
relation: this.HasManyRelation,
modelClass: SkillsClassPathModel,
join: {
from: this.tableName+'.id',
to: SkillsClassPathModel.tableName+'.levels_set_id'
}
},
related_skills_levels: {
relation: this.HasManyRelation,
modelClass: SkillsLevelsModel,
join: {
from: this.tableName+'.id',
to: SkillsLevelsModel.tableName+'.level_set_id'
}
}
};
}
}
module.exports.SkillsLevelsSetModel = SkillsLevelsSetModel;
================================================
FILE: generated-entities/models/objection-js/skills-owners-class-path-model.js
================================================
/**
*
* Reldens - SkillsOwnersClassPathModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsOwnersClassPathModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_owners_class_path';
}
static get relationMappings()
{
const { SkillsClassPathModel } = require('./skills-class-path-model');
const { PlayersModel } = require('./players-model');
return {
related_skills_class_path: {
relation: this.BelongsToOneRelation,
modelClass: SkillsClassPathModel,
join: {
from: this.tableName+'.class_path_id',
to: SkillsClassPathModel.tableName+'.id'
}
},
related_players: {
relation: this.BelongsToOneRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.owner_id',
to: PlayersModel.tableName+'.id'
}
}
};
}
}
module.exports.SkillsOwnersClassPathModel = SkillsOwnersClassPathModel;
================================================
FILE: generated-entities/models/objection-js/skills-skill-animations-model.js
================================================
/**
*
* Reldens - SkillsSkillAnimationsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsSkillAnimationsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_skill_animations';
}
static get relationMappings()
{
const { SkillsSkillModel } = require('./skills-skill-model');
return {
related_skills_skill: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillModel,
join: {
from: this.tableName+'.skill_id',
to: SkillsSkillModel.tableName+'.id'
}
}
};
}
}
module.exports.SkillsSkillAnimationsModel = SkillsSkillAnimationsModel;
================================================
FILE: generated-entities/models/objection-js/skills-skill-attack-model.js
================================================
/**
*
* Reldens - SkillsSkillAttackModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsSkillAttackModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_skill_attack';
}
static get relationMappings()
{
const { SkillsSkillModel } = require('./skills-skill-model');
return {
related_skills_skill: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillModel,
join: {
from: this.tableName+'.skill_id',
to: SkillsSkillModel.tableName+'.id'
}
}
};
}
}
module.exports.SkillsSkillAttackModel = SkillsSkillAttackModel;
================================================
FILE: generated-entities/models/objection-js/skills-skill-group-relation-model.js
================================================
/**
*
* Reldens - SkillsSkillGroupRelationModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsSkillGroupRelationModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_skill_group_relation';
}
static get relationMappings()
{
const { SkillsSkillModel } = require('./skills-skill-model');
const { SkillsGroupsModel } = require('./skills-groups-model');
return {
related_skills_skill: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillModel,
join: {
from: this.tableName+'.skill_id',
to: SkillsSkillModel.tableName+'.id'
}
},
related_skills_groups: {
relation: this.BelongsToOneRelation,
modelClass: SkillsGroupsModel,
join: {
from: this.tableName+'.group_id',
to: SkillsGroupsModel.tableName+'.id'
}
}
};
}
}
module.exports.SkillsSkillGroupRelationModel = SkillsSkillGroupRelationModel;
================================================
FILE: generated-entities/models/objection-js/skills-skill-model.js
================================================
/**
*
* Reldens - SkillsSkillModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsSkillModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_skill';
}
static get relationMappings()
{
const { SkillsSkillTypeModel } = require('./skills-skill-type-model');
const { ObjectsSkillsModel } = require('./objects-skills-model');
const { SkillsClassPathLevelSkillsModel } = require('./skills-class-path-level-skills-model');
const { SkillsSkillAnimationsModel } = require('./skills-skill-animations-model');
const { SkillsSkillAttackModel } = require('./skills-skill-attack-model');
const { SkillsSkillGroupRelationModel } = require('./skills-skill-group-relation-model');
const { SkillsSkillOwnerConditionsModel } = require('./skills-skill-owner-conditions-model');
const { SkillsSkillOwnerEffectsModel } = require('./skills-skill-owner-effects-model');
const { SkillsSkillPhysicalDataModel } = require('./skills-skill-physical-data-model');
const { SkillsSkillTargetEffectsModel } = require('./skills-skill-target-effects-model');
return {
related_skills_skill_type: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillTypeModel,
join: {
from: this.tableName+'.type',
to: SkillsSkillTypeModel.tableName+'.id'
}
},
related_objects_skills: {
relation: this.HasManyRelation,
modelClass: ObjectsSkillsModel,
join: {
from: this.tableName+'.id',
to: ObjectsSkillsModel.tableName+'.skill_id'
}
},
related_skills_class_path_level_skills: {
relation: this.HasManyRelation,
modelClass: SkillsClassPathLevelSkillsModel,
join: {
from: this.tableName+'.id',
to: SkillsClassPathLevelSkillsModel.tableName+'.skill_id'
}
},
related_skills_skill_animations: {
relation: this.HasManyRelation,
modelClass: SkillsSkillAnimationsModel,
join: {
from: this.tableName+'.id',
to: SkillsSkillAnimationsModel.tableName+'.skill_id'
}
},
related_skills_skill_attack: {
relation: this.HasOneRelation,
modelClass: SkillsSkillAttackModel,
join: {
from: this.tableName+'.id',
to: SkillsSkillAttackModel.tableName+'.skill_id'
}
},
related_skills_skill_group_relation: {
relation: this.HasOneRelation,
modelClass: SkillsSkillGroupRelationModel,
join: {
from: this.tableName+'.id',
to: SkillsSkillGroupRelationModel.tableName+'.skill_id'
}
},
related_skills_skill_owner_conditions: {
relation: this.HasManyRelation,
modelClass: SkillsSkillOwnerConditionsModel,
join: {
from: this.tableName+'.id',
to: SkillsSkillOwnerConditionsModel.tableName+'.skill_id'
}
},
related_skills_skill_owner_effects: {
relation: this.HasManyRelation,
modelClass: SkillsSkillOwnerEffectsModel,
join: {
from: this.tableName+'.id',
to: SkillsSkillOwnerEffectsModel.tableName+'.skill_id'
}
},
related_skills_skill_physical_data: {
relation: this.HasOneRelation,
modelClass: SkillsSkillPhysicalDataModel,
join: {
from: this.tableName+'.id',
to: SkillsSkillPhysicalDataModel.tableName+'.skill_id'
}
},
related_skills_skill_target_effects: {
relation: this.HasManyRelation,
modelClass: SkillsSkillTargetEffectsModel,
join: {
from: this.tableName+'.id',
to: SkillsSkillTargetEffectsModel.tableName+'.skill_id'
}
}
};
}
}
module.exports.SkillsSkillModel = SkillsSkillModel;
================================================
FILE: generated-entities/models/objection-js/skills-skill-owner-conditions-model.js
================================================
/**
*
* Reldens - SkillsSkillOwnerConditionsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsSkillOwnerConditionsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_skill_owner_conditions';
}
static get relationMappings()
{
const { SkillsSkillModel } = require('./skills-skill-model');
return {
related_skills_skill: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillModel,
join: {
from: this.tableName+'.skill_id',
to: SkillsSkillModel.tableName+'.id'
}
}
};
}
}
module.exports.SkillsSkillOwnerConditionsModel = SkillsSkillOwnerConditionsModel;
================================================
FILE: generated-entities/models/objection-js/skills-skill-owner-effects-conditions-model.js
================================================
/**
*
* Reldens - SkillsSkillOwnerEffectsConditionsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsSkillOwnerEffectsConditionsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_skill_owner_effects_conditions';
}
static get relationMappings()
{
const { SkillsSkillOwnerEffectsModel } = require('./skills-skill-owner-effects-model');
return {
related_skills_skill_owner_effects: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillOwnerEffectsModel,
join: {
from: this.tableName+'.skill_owner_effect_id',
to: SkillsSkillOwnerEffectsModel.tableName+'.id'
}
}
};
}
}
module.exports.SkillsSkillOwnerEffectsConditionsModel = SkillsSkillOwnerEffectsConditionsModel;
================================================
FILE: generated-entities/models/objection-js/skills-skill-owner-effects-model.js
================================================
/**
*
* Reldens - SkillsSkillOwnerEffectsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsSkillOwnerEffectsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_skill_owner_effects';
}
static get relationMappings()
{
const { SkillsSkillModel } = require('./skills-skill-model');
const { OperationTypesModel } = require('./operation-types-model');
const { SkillsSkillOwnerEffectsConditionsModel } = require('./skills-skill-owner-effects-conditions-model');
return {
related_skills_skill: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillModel,
join: {
from: this.tableName+'.skill_id',
to: SkillsSkillModel.tableName+'.id'
}
},
related_operation_types: {
relation: this.BelongsToOneRelation,
modelClass: OperationTypesModel,
join: {
from: this.tableName+'.operation',
to: OperationTypesModel.tableName+'.key'
}
},
related_skills_skill_owner_effects_conditions: {
relation: this.HasManyRelation,
modelClass: SkillsSkillOwnerEffectsConditionsModel,
join: {
from: this.tableName+'.id',
to: SkillsSkillOwnerEffectsConditionsModel.tableName+'.skill_owner_effect_id'
}
}
};
}
}
module.exports.SkillsSkillOwnerEffectsModel = SkillsSkillOwnerEffectsModel;
================================================
FILE: generated-entities/models/objection-js/skills-skill-physical-data-model.js
================================================
/**
*
* Reldens - SkillsSkillPhysicalDataModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsSkillPhysicalDataModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_skill_physical_data';
}
static get relationMappings()
{
const { SkillsSkillModel } = require('./skills-skill-model');
return {
related_skills_skill: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillModel,
join: {
from: this.tableName+'.skill_id',
to: SkillsSkillModel.tableName+'.id'
}
}
};
}
}
module.exports.SkillsSkillPhysicalDataModel = SkillsSkillPhysicalDataModel;
================================================
FILE: generated-entities/models/objection-js/skills-skill-target-effects-conditions-model.js
================================================
/**
*
* Reldens - SkillsSkillTargetEffectsConditionsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsSkillTargetEffectsConditionsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_skill_target_effects_conditions';
}
static get relationMappings()
{
const { SkillsSkillTargetEffectsModel } = require('./skills-skill-target-effects-model');
return {
related_skills_skill_target_effects: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillTargetEffectsModel,
join: {
from: this.tableName+'.skill_target_effect_id',
to: SkillsSkillTargetEffectsModel.tableName+'.id'
}
}
};
}
}
module.exports.SkillsSkillTargetEffectsConditionsModel = SkillsSkillTargetEffectsConditionsModel;
================================================
FILE: generated-entities/models/objection-js/skills-skill-target-effects-model.js
================================================
/**
*
* Reldens - SkillsSkillTargetEffectsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsSkillTargetEffectsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_skill_target_effects';
}
static get relationMappings()
{
const { SkillsSkillModel } = require('./skills-skill-model');
const { OperationTypesModel } = require('./operation-types-model');
const { SkillsSkillTargetEffectsConditionsModel } = require('./skills-skill-target-effects-conditions-model');
return {
related_skills_skill: {
relation: this.BelongsToOneRelation,
modelClass: SkillsSkillModel,
join: {
from: this.tableName+'.skill_id',
to: SkillsSkillModel.tableName+'.id'
}
},
related_operation_types: {
relation: this.BelongsToOneRelation,
modelClass: OperationTypesModel,
join: {
from: this.tableName+'.operation',
to: OperationTypesModel.tableName+'.key'
}
},
related_skills_skill_target_effects_conditions: {
relation: this.HasManyRelation,
modelClass: SkillsSkillTargetEffectsConditionsModel,
join: {
from: this.tableName+'.id',
to: SkillsSkillTargetEffectsConditionsModel.tableName+'.skill_target_effect_id'
}
}
};
}
}
module.exports.SkillsSkillTargetEffectsModel = SkillsSkillTargetEffectsModel;
================================================
FILE: generated-entities/models/objection-js/skills-skill-type-model.js
================================================
/**
*
* Reldens - SkillsSkillTypeModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SkillsSkillTypeModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'skills_skill_type';
}
static get relationMappings()
{
const { SkillsSkillModel } = require('./skills-skill-model');
return {
related_skills_skill: {
relation: this.HasManyRelation,
modelClass: SkillsSkillModel,
join: {
from: this.tableName+'.id',
to: SkillsSkillModel.tableName+'.type'
}
}
};
}
}
module.exports.SkillsSkillTypeModel = SkillsSkillTypeModel;
================================================
FILE: generated-entities/models/objection-js/snippets-model.js
================================================
/**
*
* Reldens - SnippetsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class SnippetsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'snippets';
}
static get relationMappings()
{
const { LocaleModel } = require('./locale-model');
return {
related_locale: {
relation: this.BelongsToOneRelation,
modelClass: LocaleModel,
join: {
from: this.tableName+'.locale_id',
to: LocaleModel.tableName+'.id'
}
}
};
}
}
module.exports.SnippetsModel = SnippetsModel;
================================================
FILE: generated-entities/models/objection-js/stats-model.js
================================================
/**
*
* Reldens - StatsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class StatsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'stats';
}
static get relationMappings()
{
const { ObjectsStatsModel } = require('./objects-stats-model');
const { PlayersStatsModel } = require('./players-stats-model');
return {
related_objects_stats: {
relation: this.HasManyRelation,
modelClass: ObjectsStatsModel,
join: {
from: this.tableName+'.id',
to: ObjectsStatsModel.tableName+'.stat_id'
}
},
related_players_stats: {
relation: this.HasManyRelation,
modelClass: PlayersStatsModel,
join: {
from: this.tableName+'.id',
to: PlayersStatsModel.tableName+'.stat_id'
}
}
};
}
}
module.exports.StatsModel = StatsModel;
================================================
FILE: generated-entities/models/objection-js/target-options-model.js
================================================
/**
*
* Reldens - TargetOptionsModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class TargetOptionsModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'target_options';
}
static get relationMappings()
{
const { ObjectsSkillsModel } = require('./objects-skills-model');
return {
related_objects_skills: {
relation: this.HasManyRelation,
modelClass: ObjectsSkillsModel,
join: {
from: this.tableName+'.id',
to: ObjectsSkillsModel.tableName+'.target_id'
}
}
};
}
}
module.exports.TargetOptionsModel = TargetOptionsModel;
================================================
FILE: generated-entities/models/objection-js/users-locale-model.js
================================================
/**
*
* Reldens - UsersLocaleModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class UsersLocaleModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'users_locale';
}
static get relationMappings()
{
const { LocaleModel } = require('./locale-model');
const { UsersModel } = require('./users-model');
return {
related_locale: {
relation: this.BelongsToOneRelation,
modelClass: LocaleModel,
join: {
from: this.tableName+'.locale_id',
to: LocaleModel.tableName+'.id'
}
},
related_users: {
relation: this.BelongsToOneRelation,
modelClass: UsersModel,
join: {
from: this.tableName+'.user_id',
to: UsersModel.tableName+'.id'
}
}
};
}
}
module.exports.UsersLocaleModel = UsersLocaleModel;
================================================
FILE: generated-entities/models/objection-js/users-login-model.js
================================================
/**
*
* Reldens - UsersLoginModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class UsersLoginModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'users_login';
}
static get relationMappings()
{
const { UsersModel } = require('./users-model');
return {
related_users: {
relation: this.BelongsToOneRelation,
modelClass: UsersModel,
join: {
from: this.tableName+'.user_id',
to: UsersModel.tableName+'.id'
}
}
};
}
}
module.exports.UsersLoginModel = UsersLoginModel;
================================================
FILE: generated-entities/models/objection-js/users-model.js
================================================
/**
*
* Reldens - UsersModel
*
*/
const { ObjectionJsRawModel } = require('@reldens/storage');
class UsersModel extends ObjectionJsRawModel
{
static get tableName()
{
return 'users';
}
static get relationMappings()
{
const { PlayersModel } = require('./players-model');
const { UsersLocaleModel } = require('./users-locale-model');
const { UsersLoginModel } = require('./users-login-model');
return {
related_players: {
relation: this.HasManyRelation,
modelClass: PlayersModel,
join: {
from: this.tableName+'.id',
to: PlayersModel.tableName+'.user_id'
}
},
related_users_locale: {
relation: this.HasManyRelation,
modelClass: UsersLocaleModel,
join: {
from: this.tableName+'.id',
to: UsersLocaleModel.tableName+'.user_id'
}
},
related_users_login: {
relation: this.HasManyRelation,
modelClass: UsersLoginModel,
join: {
from: this.tableName+'.id',
to: UsersLoginModel.tableName+'.user_id'
}
}
};
}
}
module.exports.UsersModel = UsersModel;
================================================
FILE: generated-entities/models/prisma/ads-banner-model.js
================================================
/**
*
* Reldens - AdsBannerModel
*
*/
class AdsBannerModel
{
constructor(id, ads_id, banner_data)
{
this.id = id;
this.ads_id = ads_id;
this.banner_data = banner_data;
}
static get tableName()
{
return 'ads_banner';
}
static get relationTypes()
{
return {
ads: 'one'
};
}
static get relationMappings()
{
return {
'related_ads': 'ads'
};
}
}
module.exports.AdsBannerModel = AdsBannerModel;
================================================
FILE: generated-entities/models/prisma/ads-event-video-model.js
================================================
/**
*
* Reldens - AdsEventVideoModel
*
*/
class AdsEventVideoModel
{
constructor(id, ads_id, event_key, event_data)
{
this.id = id;
this.ads_id = ads_id;
this.event_key = event_key;
this.event_data = event_data;
}
static get tableName()
{
return 'ads_event_video';
}
static get relationTypes()
{
return {
ads: 'one'
};
}
static get relationMappings()
{
return {
'related_ads': 'ads'
};
}
}
module.exports.AdsEventVideoModel = AdsEventVideoModel;
================================================
FILE: generated-entities/models/prisma/ads-model.js
================================================
/**
*
* Reldens - AdsModel
*
*/
class AdsModel
{
constructor(id, key, provider_id, type_id, width, height, position, top, bottom, left, right, replay, enabled, created_at, updated_at)
{
this.id = id;
this.key = key;
this.provider_id = provider_id;
this.type_id = type_id;
this.width = width;
this.height = height;
this.position = position;
this.top = top;
this.bottom = bottom;
this.left = left;
this.right = right;
this.replay = replay;
this.enabled = enabled;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'ads';
}
static get relationTypes()
{
return {
ads_providers: 'one',
ads_types: 'one',
ads_banner: 'one',
ads_event_video: 'one',
ads_played: 'many'
};
}
static get relationMappings()
{
return {
'related_ads_providers': 'ads_providers',
'related_ads_types': 'ads_types',
'related_ads_banner': 'ads_banner',
'related_ads_event_video': 'ads_event_video',
'related_ads_played': 'ads_played'
};
}
}
module.exports.AdsModel = AdsModel;
================================================
FILE: generated-entities/models/prisma/ads-played-model.js
================================================
/**
*
* Reldens - AdsPlayedModel
*
*/
class AdsPlayedModel
{
constructor(id, ads_id, player_id, started_at, ended_at)
{
this.id = id;
this.ads_id = ads_id;
this.player_id = player_id;
this.started_at = started_at;
this.ended_at = ended_at;
}
static get tableName()
{
return 'ads_played';
}
static get relationTypes()
{
return {
ads: 'one',
players: 'one'
};
}
static get relationMappings()
{
return {
'related_ads': 'ads',
'related_players': 'players'
};
}
}
module.exports.AdsPlayedModel = AdsPlayedModel;
================================================
FILE: generated-entities/models/prisma/ads-providers-model.js
================================================
/**
*
* Reldens - AdsProvidersModel
*
*/
class AdsProvidersModel
{
constructor(id, key, enabled)
{
this.id = id;
this.key = key;
this.enabled = enabled;
}
static get tableName()
{
return 'ads_providers';
}
static get relationTypes()
{
return {
ads: 'many'
};
}
static get relationMappings()
{
return {
'related_ads': 'ads'
};
}
}
module.exports.AdsProvidersModel = AdsProvidersModel;
================================================
FILE: generated-entities/models/prisma/ads-types-model.js
================================================
/**
*
* Reldens - AdsTypesModel
*
*/
class AdsTypesModel
{
constructor(id, key)
{
this.id = id;
this.key = key;
}
static get tableName()
{
return 'ads_types';
}
static get relationTypes()
{
return {
ads: 'many'
};
}
static get relationMappings()
{
return {
'related_ads': 'ads'
};
}
}
module.exports.AdsTypesModel = AdsTypesModel;
================================================
FILE: generated-entities/models/prisma/audio-categories-model.js
================================================
/**
*
* Reldens - AudioCategoriesModel
*
*/
class AudioCategoriesModel
{
constructor(id, category_key, category_label, enabled, single_audio, created_at, updated_at)
{
this.id = id;
this.category_key = category_key;
this.category_label = category_label;
this.enabled = enabled;
this.single_audio = single_audio;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'audio_categories';
}
static get relationTypes()
{
return {
audio: 'many',
audio_player_config: 'many'
};
}
static get relationMappings()
{
return {
'related_audio': 'audio',
'related_audio_player_config': 'audio_player_config'
};
}
}
module.exports.AudioCategoriesModel = AudioCategoriesModel;
================================================
FILE: generated-entities/models/prisma/audio-markers-model.js
================================================
/**
*
* Reldens - AudioMarkersModel
*
*/
class AudioMarkersModel
{
constructor(id, audio_id, marker_key, start, duration, config)
{
this.id = id;
this.audio_id = audio_id;
this.marker_key = marker_key;
this.start = start;
this.duration = duration;
this.config = config;
}
static get tableName()
{
return 'audio_markers';
}
static get relationTypes()
{
return {
audio: 'one'
};
}
static get relationMappings()
{
return {
'related_audio': 'audio'
};
}
}
module.exports.AudioMarkersModel = AudioMarkersModel;
================================================
FILE: generated-entities/models/prisma/audio-model.js
================================================
/**
*
* Reldens - AudioModel
*
*/
class AudioModel
{
constructor(id, audio_key, files_name, config, room_id, category_id, enabled, created_at, updated_at)
{
this.id = id;
this.audio_key = audio_key;
this.files_name = files_name;
this.config = config;
this.room_id = room_id;
this.category_id = category_id;
this.enabled = enabled;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'audio';
}
static get relationTypes()
{
return {
audio_categories: 'one',
rooms: 'one',
audio_markers: 'many'
};
}
static get relationMappings()
{
return {
'related_rooms': 'rooms',
'related_audio_categories': 'audio_categories',
'related_audio_markers': 'audio_markers'
};
}
}
module.exports.AudioModel = AudioModel;
================================================
FILE: generated-entities/models/prisma/audio-player-config-model.js
================================================
/**
*
* Reldens - AudioPlayerConfigModel
*
*/
class AudioPlayerConfigModel
{
constructor(id, player_id, category_id, enabled)
{
this.id = id;
this.player_id = player_id;
this.category_id = category_id;
this.enabled = enabled;
}
static get tableName()
{
return 'audio_player_config';
}
static get relationTypes()
{
return {
audio_categories: 'one',
players: 'one'
};
}
static get relationMappings()
{
return {
'related_players': 'players',
'related_audio_categories': 'audio_categories'
};
}
}
module.exports.AudioPlayerConfigModel = AudioPlayerConfigModel;
================================================
FILE: generated-entities/models/prisma/chat-message-types-model.js
================================================
/**
*
* Reldens - ChatMessageTypesModel
*
*/
class ChatMessageTypesModel
{
constructor(id, key, show_tab, also_show_in_type)
{
this.id = id;
this.key = key;
this.show_tab = show_tab;
this.also_show_in_type = also_show_in_type;
}
static get tableName()
{
return 'chat_message_types';
}
static get relationTypes()
{
return {
chat: 'many',
chat_message_types: 'one',
other_chat_message_types: 'many'
};
}
static get relationMappings()
{
return {
'related_chat_message_types': 'other_chat_message_types',
'related_chat': 'chat'
};
}
}
module.exports.ChatMessageTypesModel = ChatMessageTypesModel;
================================================
FILE: generated-entities/models/prisma/chat-model.js
================================================
/**
*
* Reldens - ChatModel
*
*/
class ChatModel
{
constructor(id, player_id, room_id, message, private_player_id, message_type, message_time)
{
this.id = id;
this.player_id = player_id;
this.room_id = room_id;
this.message = message;
this.private_player_id = private_player_id;
this.message_type = message_type;
this.message_time = message_time;
}
static get tableName()
{
return 'chat';
}
static get relationTypes()
{
return {
players_chat_player_idToplayers: 'one',
players_chat_private_player_idToplayers: 'one',
rooms: 'one',
chat_message_types: 'one'
};
}
static get relationMappings()
{
return {
'related_players_player': 'players_chat_player_idToplayers',
'related_rooms': 'rooms',
'related_players_private_player': 'players_chat_private_player_idToplayers',
'related_chat_message_types': 'chat_message_types'
};
}
}
module.exports.ChatModel = ChatModel;
================================================
FILE: generated-entities/models/prisma/clan-levels-model.js
================================================
/**
*
* Reldens - ClanLevelsModel
*
*/
class ClanLevelsModel
{
constructor(id, key, label, required_experience)
{
this.id = id;
this.key = key;
this.label = label;
this.required_experience = required_experience;
}
static get tableName()
{
return 'clan_levels';
}
static get relationTypes()
{
return {
clan: 'many',
clan_levels_modifiers: 'many'
};
}
static get relationMappings()
{
return {
'related_clan': 'clan',
'related_clan_levels_modifiers': 'clan_levels_modifiers'
};
}
}
module.exports.ClanLevelsModel = ClanLevelsModel;
================================================
FILE: generated-entities/models/prisma/clan-levels-modifiers-model.js
================================================
/**
*
* Reldens - ClanLevelsModifiersModel
*
*/
class ClanLevelsModifiersModel
{
constructor(id, level_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty)
{
this.id = id;
this.level_id = level_id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.minProperty = minProperty;
this.maxProperty = maxProperty;
}
static get tableName()
{
return 'clan_levels_modifiers';
}
static get relationTypes()
{
return {
clan_levels: 'one',
operation_types: 'one'
};
}
static get relationMappings()
{
return {
'related_clan_levels': 'clan_levels',
'related_operation_types': 'operation_types'
};
}
}
module.exports.ClanLevelsModifiersModel = ClanLevelsModifiersModel;
================================================
FILE: generated-entities/models/prisma/clan-members-model.js
================================================
/**
*
* Reldens - ClanMembersModel
*
*/
class ClanMembersModel
{
constructor(id, clan_id, player_id)
{
this.id = id;
this.clan_id = clan_id;
this.player_id = player_id;
}
static get tableName()
{
return 'clan_members';
}
static get relationTypes()
{
return {
clan: 'one',
players: 'one'
};
}
static get relationMappings()
{
return {
'related_clan': 'clan',
'related_players': 'players'
};
}
}
module.exports.ClanMembersModel = ClanMembersModel;
================================================
FILE: generated-entities/models/prisma/clan-model.js
================================================
/**
*
* Reldens - ClanModel
*
*/
class ClanModel
{
constructor(id, owner_id, name, points, level, created_at, updated_at)
{
this.id = id;
this.owner_id = owner_id;
this.name = name;
this.points = points;
this.level = level;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'clan';
}
static get relationTypes()
{
return {
clan_levels: 'one',
players: 'one',
clan_members: 'many'
};
}
static get relationMappings()
{
return {
'related_players': 'players',
'related_clan_levels': 'clan_levels',
'related_clan_members': 'clan_members'
};
}
}
module.exports.ClanModel = ClanModel;
================================================
FILE: generated-entities/models/prisma/config-model.js
================================================
/**
*
* Reldens - ConfigModel
*
*/
class ConfigModel
{
constructor(id, scope, path, value, type)
{
this.id = id;
this.scope = scope;
this.path = path;
this.value = value;
this.type = type;
}
static get tableName()
{
return 'config';
}
static get relationTypes()
{
return {
config_types: 'one'
};
}
static get relationMappings()
{
return {
'related_config_types': 'config_types'
};
}
}
module.exports.ConfigModel = ConfigModel;
================================================
FILE: generated-entities/models/prisma/config-types-model.js
================================================
/**
*
* Reldens - ConfigTypesModel
*
*/
class ConfigTypesModel
{
constructor(id, label)
{
this.id = id;
this.label = label;
}
static get tableName()
{
return 'config_types';
}
static get relationTypes()
{
return {
config: 'many'
};
}
static get relationMappings()
{
return {
'related_config': 'config'
};
}
}
module.exports.ConfigTypesModel = ConfigTypesModel;
================================================
FILE: generated-entities/models/prisma/drops-animations-model.js
================================================
/**
*
* Reldens - DropsAnimationsModel
*
*/
class DropsAnimationsModel
{
constructor(id, item_id, asset_type, asset_key, file, extra_params)
{
this.id = id;
this.item_id = item_id;
this.asset_type = asset_type;
this.asset_key = asset_key;
this.file = file;
this.extra_params = extra_params;
}
static get tableName()
{
return 'drops_animations';
}
static get relationTypes()
{
return {
items_item: 'one'
};
}
static get relationMappings()
{
return {
'related_items_item': 'items_item'
};
}
}
module.exports.DropsAnimationsModel = DropsAnimationsModel;
================================================
FILE: generated-entities/models/prisma/features-model.js
================================================
/**
*
* Reldens - FeaturesModel
*
*/
class FeaturesModel
{
constructor(id, code, title, is_enabled)
{
this.id = id;
this.code = code;
this.title = title;
this.is_enabled = is_enabled;
}
static get tableName()
{
return 'features';
}
}
module.exports.FeaturesModel = FeaturesModel;
================================================
FILE: generated-entities/models/prisma/items-group-model.js
================================================
/**
*
* Reldens - ItemsGroupModel
*
*/
class ItemsGroupModel
{
constructor(id, key, label, description, files_name, sort, items_limit, limit_per_item)
{
this.id = id;
this.key = key;
this.label = label;
this.description = description;
this.files_name = files_name;
this.sort = sort;
this.items_limit = items_limit;
this.limit_per_item = limit_per_item;
}
static get tableName()
{
return 'items_group';
}
static get relationTypes()
{
return {
items_item: 'many'
};
}
static get relationMappings()
{
return {
'related_items_item': 'items_item'
};
}
}
module.exports.ItemsGroupModel = ItemsGroupModel;
================================================
FILE: generated-entities/models/prisma/items-inventory-model.js
================================================
/**
*
* Reldens - ItemsInventoryModel
*
*/
class ItemsInventoryModel
{
constructor(id, owner_id, item_id, qty, remaining_uses, is_active)
{
this.id = id;
this.owner_id = owner_id;
this.item_id = item_id;
this.qty = qty;
this.remaining_uses = remaining_uses;
this.is_active = is_active;
}
static get tableName()
{
return 'items_inventory';
}
static get relationTypes()
{
return {
items_item: 'one',
players: 'one'
};
}
static get relationMappings()
{
return {
'related_players': 'players',
'related_items_item': 'items_item'
};
}
}
module.exports.ItemsInventoryModel = ItemsInventoryModel;
================================================
FILE: generated-entities/models/prisma/items-item-model.js
================================================
/**
*
* Reldens - ItemsItemModel
*
*/
class ItemsItemModel
{
constructor(id, key, type, group_id, label, description, qty_limit, uses_limit, useTimeOut, execTimeOut, customData, created_at, updated_at)
{
this.id = id;
this.key = key;
this.type = type;
this.group_id = group_id;
this.label = label;
this.description = description;
this.qty_limit = qty_limit;
this.uses_limit = uses_limit;
this.useTimeOut = useTimeOut;
this.execTimeOut = execTimeOut;
this.customData = customData;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'items_item';
}
static get relationTypes()
{
return {
drops_animations: 'one',
items_inventory: 'many',
items_group: 'one',
items_types: 'one',
items_item_modifiers: 'many',
objects_items_inventory: 'many',
objects_items_requirements_objects_items_requirements_item_keyToitems_item: 'many',
objects_items_requirements_objects_items_requirements_required_item_keyToitems_item: 'many',
objects_items_rewards_objects_items_rewards_item_keyToitems_item: 'many',
objects_items_rewards_objects_items_rewards_reward_item_keyToitems_item: 'many',
rewards: 'many'
};
}
static get relationMappings()
{
return {
'related_items_types': 'items_types',
'related_items_group': 'items_group',
'related_drops_animations': 'drops_animations',
'related_items_inventory': 'items_inventory',
'related_items_item_modifiers': 'items_item_modifiers',
'related_objects_items_inventory': 'objects_items_inventory',
'related_objects_items_requirements_item_key': 'objects_items_requirements_objects_items_requirements_item_keyToitems_item',
'related_objects_items_requirements_required_item_key': 'objects_items_requirements_objects_items_requirements_required_item_keyToitems_item',
'related_objects_items_rewards_item_key': 'objects_items_rewards_objects_items_rewards_item_keyToitems_item',
'related_objects_items_rewards_reward_item_key': 'objects_items_rewards_objects_items_rewards_reward_item_keyToitems_item',
'related_rewards': 'rewards'
};
}
}
module.exports.ItemsItemModel = ItemsItemModel;
================================================
FILE: generated-entities/models/prisma/items-item-modifiers-model.js
================================================
/**
*
* Reldens - ItemsItemModifiersModel
*
*/
class ItemsItemModifiersModel
{
constructor(id, item_id, key, property_key, operation, value, maxProperty)
{
this.id = id;
this.item_id = item_id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.maxProperty = maxProperty;
}
static get tableName()
{
return 'items_item_modifiers';
}
static get relationTypes()
{
return {
items_item: 'one',
operation_types: 'one'
};
}
static get relationMappings()
{
return {
'related_items_item': 'items_item',
'related_operation_types': 'operation_types'
};
}
}
module.exports.ItemsItemModifiersModel = ItemsItemModifiersModel;
================================================
FILE: generated-entities/models/prisma/items-types-model.js
================================================
/**
*
* Reldens - ItemsTypesModel
*
*/
class ItemsTypesModel
{
constructor(id, key)
{
this.id = id;
this.key = key;
}
static get tableName()
{
return 'items_types';
}
static get relationTypes()
{
return {
items_item: 'many'
};
}
static get relationMappings()
{
return {
'related_items_item': 'items_item'
};
}
}
module.exports.ItemsTypesModel = ItemsTypesModel;
================================================
FILE: generated-entities/models/prisma/locale-model.js
================================================
/**
*
* Reldens - LocaleModel
*
*/
class LocaleModel
{
constructor(id, locale, language_code, country_code, enabled)
{
this.id = id;
this.locale = locale;
this.language_code = language_code;
this.country_code = country_code;
this.enabled = enabled;
}
static get tableName()
{
return 'locale';
}
static get relationTypes()
{
return {
snippets: 'many',
users_locale: 'many'
};
}
static get relationMappings()
{
return {
'related_snippets': 'snippets',
'related_users_locale': 'users_locale'
};
}
}
module.exports.LocaleModel = LocaleModel;
================================================
FILE: generated-entities/models/prisma/objects-animations-model.js
================================================
/**
*
* Reldens - ObjectsAnimationsModel
*
*/
class ObjectsAnimationsModel
{
constructor(id, object_id, animationKey, animationData)
{
this.id = id;
this.object_id = object_id;
this.animationKey = animationKey;
this.animationData = animationData;
}
static get tableName()
{
return 'objects_animations';
}
static get relationTypes()
{
return {
objects: 'one'
};
}
static get relationMappings()
{
return {
'related_objects': 'objects'
};
}
}
module.exports.ObjectsAnimationsModel = ObjectsAnimationsModel;
================================================
FILE: generated-entities/models/prisma/objects-assets-model.js
================================================
/**
*
* Reldens - ObjectsAssetsModel
*
*/
class ObjectsAssetsModel
{
constructor(object_asset_id, object_id, asset_type, asset_key, asset_file, extra_params)
{
this.object_asset_id = object_asset_id;
this.object_id = object_id;
this.asset_type = asset_type;
this.asset_key = asset_key;
this.asset_file = asset_file;
this.extra_params = extra_params;
}
static get tableName()
{
return 'objects_assets';
}
static get relationTypes()
{
return {
objects: 'one'
};
}
static get relationMappings()
{
return {
'related_objects': 'objects'
};
}
}
module.exports.ObjectsAssetsModel = ObjectsAssetsModel;
================================================
FILE: generated-entities/models/prisma/objects-items-inventory-model.js
================================================
/**
*
* Reldens - ObjectsItemsInventoryModel
*
*/
class ObjectsItemsInventoryModel
{
constructor(id, owner_id, item_id, qty, remaining_uses, is_active)
{
this.id = id;
this.owner_id = owner_id;
this.item_id = item_id;
this.qty = qty;
this.remaining_uses = remaining_uses;
this.is_active = is_active;
}
static get tableName()
{
return 'objects_items_inventory';
}
static get relationTypes()
{
return {
items_item: 'one',
objects: 'one'
};
}
static get relationMappings()
{
return {
'related_objects': 'objects',
'related_items_item': 'items_item'
};
}
}
module.exports.ObjectsItemsInventoryModel = ObjectsItemsInventoryModel;
================================================
FILE: generated-entities/models/prisma/objects-items-requirements-model.js
================================================
/**
*
* Reldens - ObjectsItemsRequirementsModel
*
*/
class ObjectsItemsRequirementsModel
{
constructor(id, object_id, item_key, required_item_key, required_quantity, auto_remove_requirement)
{
this.id = id;
this.object_id = object_id;
this.item_key = item_key;
this.required_item_key = required_item_key;
this.required_quantity = required_quantity;
this.auto_remove_requirement = auto_remove_requirement;
}
static get tableName()
{
return 'objects_items_requirements';
}
static get relationTypes()
{
return {
items_item_objects_items_requirements_item_keyToitems_item: 'one',
items_item_objects_items_requirements_required_item_keyToitems_item: 'one',
objects: 'one'
};
}
static get relationMappings()
{
return {
'related_objects': 'objects',
'related_items_item_item_key': 'items_item_objects_items_requirements_item_keyToitems_item',
'related_items_item_required_item_key': 'items_item_objects_items_requirements_required_item_keyToitems_item'
};
}
}
module.exports.ObjectsItemsRequirementsModel = ObjectsItemsRequirementsModel;
================================================
FILE: generated-entities/models/prisma/objects-items-rewards-model.js
================================================
/**
*
* Reldens - ObjectsItemsRewardsModel
*
*/
class ObjectsItemsRewardsModel
{
constructor(id, object_id, item_key, reward_item_key, reward_quantity, reward_item_is_required)
{
this.id = id;
this.object_id = object_id;
this.item_key = item_key;
this.reward_item_key = reward_item_key;
this.reward_quantity = reward_quantity;
this.reward_item_is_required = reward_item_is_required;
}
static get tableName()
{
return 'objects_items_rewards';
}
static get relationTypes()
{
return {
items_item_objects_items_rewards_item_keyToitems_item: 'one',
items_item_objects_items_rewards_reward_item_keyToitems_item: 'one',
objects: 'one'
};
}
static get relationMappings()
{
return {
'related_objects': 'objects',
'related_items_item_item_key': 'items_item_objects_items_rewards_item_keyToitems_item',
'related_items_item_reward_item_key': 'items_item_objects_items_rewards_reward_item_keyToitems_item'
};
}
}
module.exports.ObjectsItemsRewardsModel = ObjectsItemsRewardsModel;
================================================
FILE: generated-entities/models/prisma/objects-model.js
================================================
/**
*
* Reldens - ObjectsModel
*
*/
class ObjectsModel
{
constructor(id, room_id, layer_name, tile_index, class_type, object_class_key, client_key, title, private_params, client_params, enabled, created_at, updated_at)
{
this.id = id;
this.room_id = room_id;
this.layer_name = layer_name;
this.tile_index = tile_index;
this.class_type = class_type;
this.object_class_key = object_class_key;
this.client_key = client_key;
this.title = title;
this.private_params = private_params;
this.client_params = client_params;
this.enabled = enabled;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'objects';
}
static get relationTypes()
{
return {
objects_types: 'one',
rooms: 'one',
objects_animations: 'many',
objects_assets: 'many',
objects_items_inventory: 'many',
objects_items_requirements: 'many',
objects_items_rewards: 'many',
objects_skills: 'many',
objects_stats: 'many',
respawn: 'many',
rewards: 'many'
};
}
static get relationMappings()
{
return {
'related_rooms': 'rooms',
'related_objects_types': 'objects_types',
'related_objects_animations': 'objects_animations',
'related_objects_assets': 'objects_assets',
'related_objects_items_inventory': 'objects_items_inventory',
'related_objects_items_requirements': 'objects_items_requirements',
'related_objects_items_rewards': 'objects_items_rewards',
'related_objects_skills': 'objects_skills',
'related_objects_stats': 'objects_stats',
'related_respawn': 'respawn',
'related_rewards': 'rewards'
};
}
}
module.exports.ObjectsModel = ObjectsModel;
================================================
FILE: generated-entities/models/prisma/objects-skills-model.js
================================================
/**
*
* Reldens - ObjectsSkillsModel
*
*/
class ObjectsSkillsModel
{
constructor(id, object_id, skill_id, target_id)
{
this.id = id;
this.object_id = object_id;
this.skill_id = skill_id;
this.target_id = target_id;
}
static get tableName()
{
return 'objects_skills';
}
static get relationTypes()
{
return {
objects: 'one',
skills_skill: 'one',
target_options: 'one'
};
}
static get relationMappings()
{
return {
'related_objects': 'objects',
'related_skills_skill': 'skills_skill',
'related_target_options': 'target_options'
};
}
}
module.exports.ObjectsSkillsModel = ObjectsSkillsModel;
================================================
FILE: generated-entities/models/prisma/objects-stats-model.js
================================================
/**
*
* Reldens - ObjectsStatsModel
*
*/
class ObjectsStatsModel
{
constructor(id, object_id, stat_id, base_value, value)
{
this.id = id;
this.object_id = object_id;
this.stat_id = stat_id;
this.base_value = base_value;
this.value = value;
}
static get tableName()
{
return 'objects_stats';
}
static get relationTypes()
{
return {
objects: 'one',
stats: 'one'
};
}
static get relationMappings()
{
return {
'related_objects': 'objects',
'related_stats': 'stats'
};
}
}
module.exports.ObjectsStatsModel = ObjectsStatsModel;
================================================
FILE: generated-entities/models/prisma/objects-types-model.js
================================================
/**
*
* Reldens - ObjectsTypesModel
*
*/
class ObjectsTypesModel
{
constructor(id, key)
{
this.id = id;
this.key = key;
}
static get tableName()
{
return 'objects_types';
}
static get relationTypes()
{
return {
objects: 'many'
};
}
static get relationMappings()
{
return {
'related_objects': 'objects'
};
}
}
module.exports.ObjectsTypesModel = ObjectsTypesModel;
================================================
FILE: generated-entities/models/prisma/operation-types-model.js
================================================
/**
*
* Reldens - OperationTypesModel
*
*/
class OperationTypesModel
{
constructor(id, label, key)
{
this.id = id;
this.label = label;
this.key = key;
}
static get tableName()
{
return 'operation_types';
}
static get relationTypes()
{
return {
clan_levels_modifiers: 'many',
items_item_modifiers: 'many',
rewards_modifiers: 'many',
skills_levels_modifiers: 'many',
skills_skill_owner_effects: 'many',
skills_skill_target_effects: 'many'
};
}
static get relationMappings()
{
return {
'related_clan_levels_modifiers': 'clan_levels_modifiers',
'related_items_item_modifiers': 'items_item_modifiers',
'related_rewards_modifiers': 'rewards_modifiers',
'related_skills_levels_modifiers': 'skills_levels_modifiers',
'related_skills_skill_owner_effects': 'skills_skill_owner_effects',
'related_skills_skill_target_effects': 'skills_skill_target_effects'
};
}
}
module.exports.OperationTypesModel = OperationTypesModel;
================================================
FILE: generated-entities/models/prisma/players-model.js
================================================
/**
*
* Reldens - PlayersModel
*
*/
class PlayersModel
{
constructor(id, user_id, name, created_at, updated_at)
{
this.id = id;
this.user_id = user_id;
this.name = name;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'players';
}
static get relationTypes()
{
return {
ads_played: 'many',
audio_player_config: 'many',
chat_chat_player_idToplayers: 'many',
chat_chat_private_player_idToplayers: 'many',
clan: 'one',
clan_members: 'one',
items_inventory: 'many',
users: 'one',
players_state: 'one',
players_stats: 'many',
rewards_events_state: 'many',
scores: 'many',
scores_detail: 'many',
skills_owners_class_path: 'many'
};
}
static get relationMappings()
{
return {
'related_users': 'users',
'related_ads_played': 'ads_played',
'related_audio_player_config': 'audio_player_config',
'related_chat_player': 'chat_chat_player_idToplayers',
'related_chat_private_player': 'chat_chat_private_player_idToplayers',
'related_clan': 'clan',
'related_clan_members': 'clan_members',
'related_items_inventory': 'items_inventory',
'related_players_state': 'players_state',
'related_players_stats': 'players_stats',
'related_rewards_events_state': 'rewards_events_state',
'related_scores': 'scores',
'related_scores_detail': 'scores_detail',
'related_skills_owners_class_path': 'skills_owners_class_path'
};
}
}
module.exports.PlayersModel = PlayersModel;
================================================
FILE: generated-entities/models/prisma/players-state-model.js
================================================
/**
*
* Reldens - PlayersStateModel
*
*/
class PlayersStateModel
{
constructor(id, player_id, room_id, x, y, dir)
{
this.id = id;
this.player_id = player_id;
this.room_id = room_id;
this.x = x;
this.y = y;
this.dir = dir;
}
static get tableName()
{
return 'players_state';
}
static get relationTypes()
{
return {
players: 'one',
rooms: 'one'
};
}
static get relationMappings()
{
return {
'related_players': 'players',
'related_rooms': 'rooms'
};
}
}
module.exports.PlayersStateModel = PlayersStateModel;
================================================
FILE: generated-entities/models/prisma/players-stats-model.js
================================================
/**
*
* Reldens - PlayersStatsModel
*
*/
class PlayersStatsModel
{
constructor(id, player_id, stat_id, base_value, value)
{
this.id = id;
this.player_id = player_id;
this.stat_id = stat_id;
this.base_value = base_value;
this.value = value;
}
static get tableName()
{
return 'players_stats';
}
static get relationTypes()
{
return {
players: 'one',
stats: 'one'
};
}
static get relationMappings()
{
return {
'related_players': 'players',
'related_stats': 'stats'
};
}
}
module.exports.PlayersStatsModel = PlayersStatsModel;
================================================
FILE: generated-entities/models/prisma/registered-models-prisma.js
================================================
/**
*
* Reldens - Registered Models
*
*/
const { AdsBannerModel } = require('./ads-banner-model');
const { AdsModel } = require('./ads-model');
const { AdsEventVideoModel } = require('./ads-event-video-model');
const { AdsPlayedModel } = require('./ads-played-model');
const { AdsProvidersModel } = require('./ads-providers-model');
const { AdsTypesModel } = require('./ads-types-model');
const { AudioCategoriesModel } = require('./audio-categories-model');
const { AudioModel } = require('./audio-model');
const { AudioMarkersModel } = require('./audio-markers-model');
const { AudioPlayerConfigModel } = require('./audio-player-config-model');
const { ChatModel } = require('./chat-model');
const { ChatMessageTypesModel } = require('./chat-message-types-model');
const { ClanModel } = require('./clan-model');
const { ClanLevelsModel } = require('./clan-levels-model');
const { ClanLevelsModifiersModel } = require('./clan-levels-modifiers-model');
const { ClanMembersModel } = require('./clan-members-model');
const { ConfigModel } = require('./config-model');
const { ConfigTypesModel } = require('./config-types-model');
const { DropsAnimationsModel } = require('./drops-animations-model');
const { FeaturesModel } = require('./features-model');
const { ItemsGroupModel } = require('./items-group-model');
const { ItemsInventoryModel } = require('./items-inventory-model');
const { ItemsItemModel } = require('./items-item-model');
const { ItemsItemModifiersModel } = require('./items-item-modifiers-model');
const { ItemsTypesModel } = require('./items-types-model');
const { LocaleModel } = require('./locale-model');
const { ObjectsAnimationsModel } = require('./objects-animations-model');
const { ObjectsAssetsModel } = require('./objects-assets-model');
const { ObjectsModel } = require('./objects-model');
const { ObjectsItemsInventoryModel } = require('./objects-items-inventory-model');
const { ObjectsItemsRequirementsModel } = require('./objects-items-requirements-model');
const { ObjectsItemsRewardsModel } = require('./objects-items-rewards-model');
const { ObjectsSkillsModel } = require('./objects-skills-model');
const { ObjectsStatsModel } = require('./objects-stats-model');
const { ObjectsTypesModel } = require('./objects-types-model');
const { OperationTypesModel } = require('./operation-types-model');
const { PlayersModel } = require('./players-model');
const { PlayersStateModel } = require('./players-state-model');
const { PlayersStatsModel } = require('./players-stats-model');
const { RespawnModel } = require('./respawn-model');
const { RewardsModel } = require('./rewards-model');
const { RewardsEventsModel } = require('./rewards-events-model');
const { RewardsEventsStateModel } = require('./rewards-events-state-model');
const { RewardsModifiersModel } = require('./rewards-modifiers-model');
const { RoomsChangePointsModel } = require('./rooms-change-points-model');
const { RoomsModel } = require('./rooms-model');
const { RoomsReturnPointsModel } = require('./rooms-return-points-model');
const { ScoresDetailModel } = require('./scores-detail-model');
const { ScoresModel } = require('./scores-model');
const { SkillsClassLevelUpAnimationsModel } = require('./skills-class-level-up-animations-model');
const { SkillsClassPathModel } = require('./skills-class-path-model');
const { SkillsClassPathLevelLabelsModel } = require('./skills-class-path-level-labels-model');
const { SkillsClassPathLevelSkillsModel } = require('./skills-class-path-level-skills-model');
const { SkillsGroupsModel } = require('./skills-groups-model');
const { SkillsLevelsModel } = require('./skills-levels-model');
const { SkillsLevelsModifiersConditionsModel } = require('./skills-levels-modifiers-conditions-model');
const { SkillsLevelsModifiersModel } = require('./skills-levels-modifiers-model');
const { SkillsLevelsSetModel } = require('./skills-levels-set-model');
const { SkillsOwnersClassPathModel } = require('./skills-owners-class-path-model');
const { SkillsSkillAnimationsModel } = require('./skills-skill-animations-model');
const { SkillsSkillAttackModel } = require('./skills-skill-attack-model');
const { SkillsSkillModel } = require('./skills-skill-model');
const { SkillsSkillGroupRelationModel } = require('./skills-skill-group-relation-model');
const { SkillsSkillOwnerConditionsModel } = require('./skills-skill-owner-conditions-model');
const { SkillsSkillOwnerEffectsConditionsModel } = require('./skills-skill-owner-effects-conditions-model');
const { SkillsSkillOwnerEffectsModel } = require('./skills-skill-owner-effects-model');
const { SkillsSkillPhysicalDataModel } = require('./skills-skill-physical-data-model');
const { SkillsSkillTargetEffectsConditionsModel } = require('./skills-skill-target-effects-conditions-model');
const { SkillsSkillTargetEffectsModel } = require('./skills-skill-target-effects-model');
const { SkillsSkillTypeModel } = require('./skills-skill-type-model');
const { SnippetsModel } = require('./snippets-model');
const { StatsModel } = require('./stats-model');
const { TargetOptionsModel } = require('./target-options-model');
const { UsersModel } = require('./users-model');
const { UsersLocaleModel } = require('./users-locale-model');
const { UsersLoginModel } = require('./users-login-model');
const { entitiesConfig } = require('../../entities-config');
const { entitiesTranslations } = require('../../entities-translations');
let rawRegisteredEntities = {
adsBanner: AdsBannerModel,
ads: AdsModel,
adsEventVideo: AdsEventVideoModel,
adsPlayed: AdsPlayedModel,
adsProviders: AdsProvidersModel,
adsTypes: AdsTypesModel,
audioCategories: AudioCategoriesModel,
audio: AudioModel,
audioMarkers: AudioMarkersModel,
audioPlayerConfig: AudioPlayerConfigModel,
chat: ChatModel,
chatMessageTypes: ChatMessageTypesModel,
clan: ClanModel,
clanLevels: ClanLevelsModel,
clanLevelsModifiers: ClanLevelsModifiersModel,
clanMembers: ClanMembersModel,
config: ConfigModel,
configTypes: ConfigTypesModel,
dropsAnimations: DropsAnimationsModel,
features: FeaturesModel,
itemsGroup: ItemsGroupModel,
itemsInventory: ItemsInventoryModel,
itemsItem: ItemsItemModel,
itemsItemModifiers: ItemsItemModifiersModel,
itemsTypes: ItemsTypesModel,
locale: LocaleModel,
objectsAnimations: ObjectsAnimationsModel,
objectsAssets: ObjectsAssetsModel,
objects: ObjectsModel,
objectsItemsInventory: ObjectsItemsInventoryModel,
objectsItemsRequirements: ObjectsItemsRequirementsModel,
objectsItemsRewards: ObjectsItemsRewardsModel,
objectsSkills: ObjectsSkillsModel,
objectsStats: ObjectsStatsModel,
objectsTypes: ObjectsTypesModel,
operationTypes: OperationTypesModel,
players: PlayersModel,
playersState: PlayersStateModel,
playersStats: PlayersStatsModel,
respawn: RespawnModel,
rewards: RewardsModel,
rewardsEvents: RewardsEventsModel,
rewardsEventsState: RewardsEventsStateModel,
rewardsModifiers: RewardsModifiersModel,
roomsChangePoints: RoomsChangePointsModel,
rooms: RoomsModel,
roomsReturnPoints: RoomsReturnPointsModel,
scoresDetail: ScoresDetailModel,
scores: ScoresModel,
skillsClassLevelUpAnimations: SkillsClassLevelUpAnimationsModel,
skillsClassPath: SkillsClassPathModel,
skillsClassPathLevelLabels: SkillsClassPathLevelLabelsModel,
skillsClassPathLevelSkills: SkillsClassPathLevelSkillsModel,
skillsGroups: SkillsGroupsModel,
skillsLevels: SkillsLevelsModel,
skillsLevelsModifiersConditions: SkillsLevelsModifiersConditionsModel,
skillsLevelsModifiers: SkillsLevelsModifiersModel,
skillsLevelsSet: SkillsLevelsSetModel,
skillsOwnersClassPath: SkillsOwnersClassPathModel,
skillsSkillAnimations: SkillsSkillAnimationsModel,
skillsSkillAttack: SkillsSkillAttackModel,
skillsSkill: SkillsSkillModel,
skillsSkillGroupRelation: SkillsSkillGroupRelationModel,
skillsSkillOwnerConditions: SkillsSkillOwnerConditionsModel,
skillsSkillOwnerEffectsConditions: SkillsSkillOwnerEffectsConditionsModel,
skillsSkillOwnerEffects: SkillsSkillOwnerEffectsModel,
skillsSkillPhysicalData: SkillsSkillPhysicalDataModel,
skillsSkillTargetEffectsConditions: SkillsSkillTargetEffectsConditionsModel,
skillsSkillTargetEffects: SkillsSkillTargetEffectsModel,
skillsSkillType: SkillsSkillTypeModel,
snippets: SnippetsModel,
stats: StatsModel,
targetOptions: TargetOptionsModel,
users: UsersModel,
usersLocale: UsersLocaleModel,
usersLogin: UsersLoginModel
};
module.exports.rawRegisteredEntities = rawRegisteredEntities;
module.exports.entitiesConfig = entitiesConfig;
module.exports.entitiesTranslations = entitiesTranslations;
================================================
FILE: generated-entities/models/prisma/respawn-model.js
================================================
/**
*
* Reldens - RespawnModel
*
*/
class RespawnModel
{
constructor(id, object_id, respawn_time, instances_limit, layer, created_at, updated_at)
{
this.id = id;
this.object_id = object_id;
this.respawn_time = respawn_time;
this.instances_limit = instances_limit;
this.layer = layer;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'respawn';
}
static get relationTypes()
{
return {
objects: 'one'
};
}
static get relationMappings()
{
return {
'related_objects': 'objects'
};
}
}
module.exports.RespawnModel = RespawnModel;
================================================
FILE: generated-entities/models/prisma/rewards-events-model.js
================================================
/**
*
* Reldens - RewardsEventsModel
*
*/
class RewardsEventsModel
{
constructor(id, label, description, handler_key, event_key, event_data, position, enabled, active_from, active_to)
{
this.id = id;
this.label = label;
this.description = description;
this.handler_key = handler_key;
this.event_key = event_key;
this.event_data = event_data;
this.position = position;
this.enabled = enabled;
this.active_from = active_from;
this.active_to = active_to;
}
static get tableName()
{
return 'rewards_events';
}
static get relationTypes()
{
return {
rewards_events_state: 'many'
};
}
static get relationMappings()
{
return {
'related_rewards_events_state': 'rewards_events_state'
};
}
}
module.exports.RewardsEventsModel = RewardsEventsModel;
================================================
FILE: generated-entities/models/prisma/rewards-events-state-model.js
================================================
/**
*
* Reldens - RewardsEventsStateModel
*
*/
class RewardsEventsStateModel
{
constructor(id, rewards_events_id, player_id, state)
{
this.id = id;
this.rewards_events_id = rewards_events_id;
this.player_id = player_id;
this.state = state;
}
static get tableName()
{
return 'rewards_events_state';
}
static get relationTypes()
{
return {
rewards_events: 'one',
players: 'one'
};
}
static get relationMappings()
{
return {
'related_rewards_events': 'rewards_events',
'related_players': 'players'
};
}
}
module.exports.RewardsEventsStateModel = RewardsEventsStateModel;
================================================
FILE: generated-entities/models/prisma/rewards-model.js
================================================
/**
*
* Reldens - RewardsModel
*
*/
class RewardsModel
{
constructor(id, object_id, item_id, modifier_id, experience, drop_rate, drop_quantity, is_unique, was_given, has_drop_body, created_at, updated_at)
{
this.id = id;
this.object_id = object_id;
this.item_id = item_id;
this.modifier_id = modifier_id;
this.experience = experience;
this.drop_rate = drop_rate;
this.drop_quantity = drop_quantity;
this.is_unique = is_unique;
this.was_given = was_given;
this.has_drop_body = has_drop_body;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'rewards';
}
static get relationTypes()
{
return {
items_item: 'one',
objects: 'one',
rewards_modifiers: 'one'
};
}
static get relationMappings()
{
return {
'related_objects': 'objects',
'related_items_item': 'items_item',
'related_rewards_modifiers': 'rewards_modifiers'
};
}
}
module.exports.RewardsModel = RewardsModel;
================================================
FILE: generated-entities/models/prisma/rewards-modifiers-model.js
================================================
/**
*
* Reldens - RewardsModifiersModel
*
*/
class RewardsModifiersModel
{
constructor(id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty)
{
this.id = id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.minProperty = minProperty;
this.maxProperty = maxProperty;
}
static get tableName()
{
return 'rewards_modifiers';
}
static get relationTypes()
{
return {
rewards: 'many',
operation_types: 'one'
};
}
static get relationMappings()
{
return {
'related_operation_types': 'operation_types',
'related_rewards': 'rewards'
};
}
}
module.exports.RewardsModifiersModel = RewardsModifiersModel;
================================================
FILE: generated-entities/models/prisma/rooms-change-points-model.js
================================================
/**
*
* Reldens - RoomsChangePointsModel
*
*/
class RoomsChangePointsModel
{
constructor(id, room_id, tile_index, next_room_id)
{
this.id = id;
this.room_id = room_id;
this.tile_index = tile_index;
this.next_room_id = next_room_id;
}
static get tableName()
{
return 'rooms_change_points';
}
static get relationTypes()
{
return {
rooms_rooms_change_points_room_idTorooms: 'one',
rooms_rooms_change_points_next_room_idTorooms: 'one'
};
}
static get relationMappings()
{
return {
'related_rooms_room': 'rooms_rooms_change_points_room_idTorooms',
'related_rooms_next_room': 'rooms_rooms_change_points_next_room_idTorooms',
'related_rooms_change_points_room': 'rooms_rooms_change_points_room_idTorooms',
'related_rooms_change_points_next_room': 'rooms_rooms_change_points_next_room_idTorooms'
};
}
}
module.exports.RoomsChangePointsModel = RoomsChangePointsModel;
================================================
FILE: generated-entities/models/prisma/rooms-model.js
================================================
/**
*
* Reldens - RoomsModel
*
*/
class RoomsModel
{
constructor(id, name, title, map_filename, scene_images, room_class_key, server_url, customData, created_at, updated_at)
{
this.id = id;
this.name = name;
this.title = title;
this.map_filename = map_filename;
this.scene_images = scene_images;
this.room_class_key = room_class_key;
this.server_url = server_url;
this.customData = customData;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'rooms';
}
static get relationTypes()
{
return {
audio: 'many',
chat: 'many',
objects: 'many',
players_state: 'many',
rooms_change_points_rooms_change_points_room_idTorooms: 'many',
rooms_change_points_rooms_change_points_next_room_idTorooms: 'many',
rooms_return_points_rooms_return_points_from_room_idTorooms: 'many',
rooms_return_points_rooms_return_points_room_idTorooms: 'many'
};
}
static get relationMappings()
{
return {
'related_audio': 'audio',
'related_chat': 'chat',
'related_objects': 'objects',
'related_players_state': 'players_state',
'related_rooms_change_points_room': 'rooms_change_points_rooms_change_points_room_idTorooms',
'related_rooms_change_points_next_room': 'rooms_change_points_rooms_change_points_next_room_idTorooms',
'related_rooms_return_points_from_room': 'rooms_return_points_rooms_return_points_from_room_idTorooms',
'related_rooms_return_points_room': 'rooms_return_points_rooms_return_points_room_idTorooms'
};
}
}
module.exports.RoomsModel = RoomsModel;
================================================
FILE: generated-entities/models/prisma/rooms-return-points-model.js
================================================
/**
*
* Reldens - RoomsReturnPointsModel
*
*/
class RoomsReturnPointsModel
{
constructor(id, room_id, direction, x, y, is_default, from_room_id)
{
this.id = id;
this.room_id = room_id;
this.direction = direction;
this.x = x;
this.y = y;
this.is_default = is_default;
this.from_room_id = from_room_id;
}
static get tableName()
{
return 'rooms_return_points';
}
static get relationTypes()
{
return {
rooms_rooms_return_points_from_room_idTorooms: 'one',
rooms_rooms_return_points_room_idTorooms: 'one'
};
}
static get relationMappings()
{
return {
'related_rooms_room': 'rooms_rooms_return_points_from_room_idTorooms',
'related_rooms_from_room': 'rooms_rooms_return_points_from_room_idTorooms',
'related_rooms_return_points_from_room': 'rooms_rooms_return_points_from_room_idTorooms',
'related_rooms_return_points_room': 'rooms_rooms_return_points_room_idTorooms'
};
}
}
module.exports.RoomsReturnPointsModel = RoomsReturnPointsModel;
================================================
FILE: generated-entities/models/prisma/scores-detail-model.js
================================================
/**
*
* Reldens - ScoresDetailModel
*
*/
class ScoresDetailModel
{
constructor(id, player_id, obtained_score, kill_time, kill_player_id, kill_npc_id)
{
this.id = id;
this.player_id = player_id;
this.obtained_score = obtained_score;
this.kill_time = kill_time;
this.kill_player_id = kill_player_id;
this.kill_npc_id = kill_npc_id;
}
static get tableName()
{
return 'scores_detail';
}
static get relationTypes()
{
return {
players: 'one'
};
}
static get relationMappings()
{
return {
'related_players': 'players'
};
}
}
module.exports.ScoresDetailModel = ScoresDetailModel;
================================================
FILE: generated-entities/models/prisma/scores-model.js
================================================
/**
*
* Reldens - ScoresModel
*
*/
class ScoresModel
{
constructor(id, player_id, total_score, players_kills_count, npcs_kills_count, last_player_kill_time, last_npc_kill_time, created_at, updated_at)
{
this.id = id;
this.player_id = player_id;
this.total_score = total_score;
this.players_kills_count = players_kills_count;
this.npcs_kills_count = npcs_kills_count;
this.last_player_kill_time = last_player_kill_time;
this.last_npc_kill_time = last_npc_kill_time;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'scores';
}
static get relationTypes()
{
return {
players: 'one'
};
}
static get relationMappings()
{
return {
'related_players': 'players'
};
}
}
module.exports.ScoresModel = ScoresModel;
================================================
FILE: generated-entities/models/prisma/skills-class-level-up-animations-model.js
================================================
/**
*
* Reldens - SkillsClassLevelUpAnimationsModel
*
*/
class SkillsClassLevelUpAnimationsModel
{
constructor(id, class_path_id, level_id, animationData)
{
this.id = id;
this.class_path_id = class_path_id;
this.level_id = level_id;
this.animationData = animationData;
}
static get tableName()
{
return 'skills_class_level_up_animations';
}
static get relationTypes()
{
return {
skills_class_path: 'one',
skills_levels: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_class_path': 'skills_class_path',
'related_skills_levels': 'skills_levels'
};
}
}
module.exports.SkillsClassLevelUpAnimationsModel = SkillsClassLevelUpAnimationsModel;
================================================
FILE: generated-entities/models/prisma/skills-class-path-level-labels-model.js
================================================
/**
*
* Reldens - SkillsClassPathLevelLabelsModel
*
*/
class SkillsClassPathLevelLabelsModel
{
constructor(id, class_path_id, level_id, label)
{
this.id = id;
this.class_path_id = class_path_id;
this.level_id = level_id;
this.label = label;
}
static get tableName()
{
return 'skills_class_path_level_labels';
}
static get relationTypes()
{
return {
skills_class_path: 'one',
skills_levels: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_class_path': 'skills_class_path',
'related_skills_levels': 'skills_levels'
};
}
}
module.exports.SkillsClassPathLevelLabelsModel = SkillsClassPathLevelLabelsModel;
================================================
FILE: generated-entities/models/prisma/skills-class-path-level-skills-model.js
================================================
/**
*
* Reldens - SkillsClassPathLevelSkillsModel
*
*/
class SkillsClassPathLevelSkillsModel
{
constructor(id, class_path_id, level_id, skill_id)
{
this.id = id;
this.class_path_id = class_path_id;
this.level_id = level_id;
this.skill_id = skill_id;
}
static get tableName()
{
return 'skills_class_path_level_skills';
}
static get relationTypes()
{
return {
skills_class_path: 'one',
skills_levels: 'one',
skills_skill: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_class_path': 'skills_class_path',
'related_skills_levels': 'skills_levels',
'related_skills_skill': 'skills_skill'
};
}
}
module.exports.SkillsClassPathLevelSkillsModel = SkillsClassPathLevelSkillsModel;
================================================
FILE: generated-entities/models/prisma/skills-class-path-model.js
================================================
/**
*
* Reldens - SkillsClassPathModel
*
*/
class SkillsClassPathModel
{
constructor(id, key, label, levels_set_id, enabled, created_at, updated_at)
{
this.id = id;
this.key = key;
this.label = label;
this.levels_set_id = levels_set_id;
this.enabled = enabled;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'skills_class_path';
}
static get relationTypes()
{
return {
skills_class_level_up_animations: 'many',
skills_levels_set: 'one',
skills_class_path_level_labels: 'many',
skills_class_path_level_skills: 'many',
skills_owners_class_path: 'many'
};
}
static get relationMappings()
{
return {
'related_skills_levels_set': 'skills_levels_set',
'related_skills_class_level_up_animations': 'skills_class_level_up_animations',
'related_skills_class_path_level_labels': 'skills_class_path_level_labels',
'related_skills_class_path_level_skills': 'skills_class_path_level_skills',
'related_skills_owners_class_path': 'skills_owners_class_path'
};
}
}
module.exports.SkillsClassPathModel = SkillsClassPathModel;
================================================
FILE: generated-entities/models/prisma/skills-groups-model.js
================================================
/**
*
* Reldens - SkillsGroupsModel
*
*/
class SkillsGroupsModel
{
constructor(id, key, label, description, sort)
{
this.id = id;
this.key = key;
this.label = label;
this.description = description;
this.sort = sort;
}
static get tableName()
{
return 'skills_groups';
}
static get relationTypes()
{
return {
skills_skill_group_relation: 'many'
};
}
static get relationMappings()
{
return {
'related_skills_skill_group_relation': 'skills_skill_group_relation'
};
}
}
module.exports.SkillsGroupsModel = SkillsGroupsModel;
================================================
FILE: generated-entities/models/prisma/skills-levels-model.js
================================================
/**
*
* Reldens - SkillsLevelsModel
*
*/
class SkillsLevelsModel
{
constructor(id, key, label, required_experience, level_set_id)
{
this.id = id;
this.key = key;
this.label = label;
this.required_experience = required_experience;
this.level_set_id = level_set_id;
}
static get tableName()
{
return 'skills_levels';
}
static get relationTypes()
{
return {
skills_class_level_up_animations: 'many',
skills_class_path_level_labels: 'many',
skills_class_path_level_skills: 'many',
skills_levels_set: 'one',
skills_levels_modifiers: 'many'
};
}
static get relationMappings()
{
return {
'related_skills_levels_set': 'skills_levels_set',
'related_skills_class_level_up_animations': 'skills_class_level_up_animations',
'related_skills_class_path_level_labels': 'skills_class_path_level_labels',
'related_skills_class_path_level_skills': 'skills_class_path_level_skills',
'related_skills_levels_modifiers': 'skills_levels_modifiers'
};
}
}
module.exports.SkillsLevelsModel = SkillsLevelsModel;
================================================
FILE: generated-entities/models/prisma/skills-levels-modifiers-conditions-model.js
================================================
/**
*
* Reldens - SkillsLevelsModifiersConditionsModel
*
*/
class SkillsLevelsModifiersConditionsModel
{
constructor(id, levels_modifier_id, key, property_key, conditional, value)
{
this.id = id;
this.levels_modifier_id = levels_modifier_id;
this.key = key;
this.property_key = property_key;
this.conditional = conditional;
this.value = value;
}
static get tableName()
{
return 'skills_levels_modifiers_conditions';
}
}
module.exports.SkillsLevelsModifiersConditionsModel = SkillsLevelsModifiersConditionsModel;
================================================
FILE: generated-entities/models/prisma/skills-levels-modifiers-model.js
================================================
/**
*
* Reldens - SkillsLevelsModifiersModel
*
*/
class SkillsLevelsModifiersModel
{
constructor(id, level_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty)
{
this.id = id;
this.level_id = level_id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.minProperty = minProperty;
this.maxProperty = maxProperty;
}
static get tableName()
{
return 'skills_levels_modifiers';
}
static get relationTypes()
{
return {
operation_types: 'one',
skills_levels: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_levels': 'skills_levels',
'related_operation_types': 'operation_types'
};
}
}
module.exports.SkillsLevelsModifiersModel = SkillsLevelsModifiersModel;
================================================
FILE: generated-entities/models/prisma/skills-levels-set-model.js
================================================
/**
*
* Reldens - SkillsLevelsSetModel
*
*/
class SkillsLevelsSetModel
{
constructor(id, key, label, autoFillRanges, autoFillExperienceMultiplier, created_at, updated_at)
{
this.id = id;
this.key = key;
this.label = label;
this.autoFillRanges = autoFillRanges;
this.autoFillExperienceMultiplier = autoFillExperienceMultiplier;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'skills_levels_set';
}
static get relationTypes()
{
return {
skills_class_path: 'many',
skills_levels: 'many'
};
}
static get relationMappings()
{
return {
'related_skills_class_path': 'skills_class_path',
'related_skills_levels': 'skills_levels'
};
}
}
module.exports.SkillsLevelsSetModel = SkillsLevelsSetModel;
================================================
FILE: generated-entities/models/prisma/skills-owners-class-path-model.js
================================================
/**
*
* Reldens - SkillsOwnersClassPathModel
*
*/
class SkillsOwnersClassPathModel
{
constructor(id, class_path_id, owner_id, currentLevel, currentExp)
{
this.id = id;
this.class_path_id = class_path_id;
this.owner_id = owner_id;
this.currentLevel = currentLevel;
this.currentExp = currentExp;
}
static get tableName()
{
return 'skills_owners_class_path';
}
static get relationTypes()
{
return {
players: 'one',
skills_class_path: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_class_path': 'skills_class_path',
'related_players': 'players'
};
}
}
module.exports.SkillsOwnersClassPathModel = SkillsOwnersClassPathModel;
================================================
FILE: generated-entities/models/prisma/skills-skill-animations-model.js
================================================
/**
*
* Reldens - SkillsSkillAnimationsModel
*
*/
class SkillsSkillAnimationsModel
{
constructor(id, skill_id, key, classKey, animationData)
{
this.id = id;
this.skill_id = skill_id;
this.key = key;
this.classKey = classKey;
this.animationData = animationData;
}
static get tableName()
{
return 'skills_skill_animations';
}
static get relationTypes()
{
return {
skills_skill: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_skill': 'skills_skill'
};
}
}
module.exports.SkillsSkillAnimationsModel = SkillsSkillAnimationsModel;
================================================
FILE: generated-entities/models/prisma/skills-skill-attack-model.js
================================================
/**
*
* Reldens - SkillsSkillAttackModel
*
*/
class SkillsSkillAttackModel
{
constructor(id, skill_id, affectedProperty, allowEffectBelowZero, hitDamage, applyDirectDamage, attackProperties, defenseProperties, aimProperties, dodgeProperties, dodgeFullEnabled, dodgeOverAimSuccess, damageAffected, criticalAffected)
{
this.id = id;
this.skill_id = skill_id;
this.affectedProperty = affectedProperty;
this.allowEffectBelowZero = allowEffectBelowZero;
this.hitDamage = hitDamage;
this.applyDirectDamage = applyDirectDamage;
this.attackProperties = attackProperties;
this.defenseProperties = defenseProperties;
this.aimProperties = aimProperties;
this.dodgeProperties = dodgeProperties;
this.dodgeFullEnabled = dodgeFullEnabled;
this.dodgeOverAimSuccess = dodgeOverAimSuccess;
this.damageAffected = damageAffected;
this.criticalAffected = criticalAffected;
}
static get tableName()
{
return 'skills_skill_attack';
}
static get relationTypes()
{
return {
skills_skill: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_skill': 'skills_skill'
};
}
}
module.exports.SkillsSkillAttackModel = SkillsSkillAttackModel;
================================================
FILE: generated-entities/models/prisma/skills-skill-group-relation-model.js
================================================
/**
*
* Reldens - SkillsSkillGroupRelationModel
*
*/
class SkillsSkillGroupRelationModel
{
constructor(id, skill_id, group_id)
{
this.id = id;
this.skill_id = skill_id;
this.group_id = group_id;
}
static get tableName()
{
return 'skills_skill_group_relation';
}
static get relationTypes()
{
return {
skills_groups: 'one',
skills_skill: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_skill': 'skills_skill',
'related_skills_groups': 'skills_groups'
};
}
}
module.exports.SkillsSkillGroupRelationModel = SkillsSkillGroupRelationModel;
================================================
FILE: generated-entities/models/prisma/skills-skill-model.js
================================================
/**
*
* Reldens - SkillsSkillModel
*
*/
class SkillsSkillModel
{
constructor(id, key, type, label, autoValidation, skillDelay, castTime, usesLimit, range, rangeAutomaticValidation, rangePropertyX, rangePropertyY, rangeTargetPropertyX, rangeTargetPropertyY, allowSelfTarget, criticalChance, criticalMultiplier, criticalFixedValue, customData, created_at, updated_at)
{
this.id = id;
this.key = key;
this.type = type;
this.label = label;
this.autoValidation = autoValidation;
this.skillDelay = skillDelay;
this.castTime = castTime;
this.usesLimit = usesLimit;
this.range = range;
this.rangeAutomaticValidation = rangeAutomaticValidation;
this.rangePropertyX = rangePropertyX;
this.rangePropertyY = rangePropertyY;
this.rangeTargetPropertyX = rangeTargetPropertyX;
this.rangeTargetPropertyY = rangeTargetPropertyY;
this.allowSelfTarget = allowSelfTarget;
this.criticalChance = criticalChance;
this.criticalMultiplier = criticalMultiplier;
this.criticalFixedValue = criticalFixedValue;
this.customData = customData;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'skills_skill';
}
static get relationTypes()
{
return {
objects_skills: 'many',
skills_class_path_level_skills: 'many',
skills_skill_type: 'one',
skills_skill_animations: 'many',
skills_skill_attack: 'one',
skills_skill_group_relation: 'one',
skills_skill_owner_conditions: 'many',
skills_skill_owner_effects: 'many',
skills_skill_physical_data: 'one',
skills_skill_target_effects: 'many'
};
}
static get relationMappings()
{
return {
'related_skills_skill_type': 'skills_skill_type',
'related_objects_skills': 'objects_skills',
'related_skills_class_path_level_skills': 'skills_class_path_level_skills',
'related_skills_skill_animations': 'skills_skill_animations',
'related_skills_skill_attack': 'skills_skill_attack',
'related_skills_skill_group_relation': 'skills_skill_group_relation',
'related_skills_skill_owner_conditions': 'skills_skill_owner_conditions',
'related_skills_skill_owner_effects': 'skills_skill_owner_effects',
'related_skills_skill_physical_data': 'skills_skill_physical_data',
'related_skills_skill_target_effects': 'skills_skill_target_effects'
};
}
}
module.exports.SkillsSkillModel = SkillsSkillModel;
================================================
FILE: generated-entities/models/prisma/skills-skill-owner-conditions-model.js
================================================
/**
*
* Reldens - SkillsSkillOwnerConditionsModel
*
*/
class SkillsSkillOwnerConditionsModel
{
constructor(id, skill_id, key, property_key, conditional, value)
{
this.id = id;
this.skill_id = skill_id;
this.key = key;
this.property_key = property_key;
this.conditional = conditional;
this.value = value;
}
static get tableName()
{
return 'skills_skill_owner_conditions';
}
static get relationTypes()
{
return {
skills_skill: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_skill': 'skills_skill'
};
}
}
module.exports.SkillsSkillOwnerConditionsModel = SkillsSkillOwnerConditionsModel;
================================================
FILE: generated-entities/models/prisma/skills-skill-owner-effects-conditions-model.js
================================================
/**
*
* Reldens - SkillsSkillOwnerEffectsConditionsModel
*
*/
class SkillsSkillOwnerEffectsConditionsModel
{
constructor(id, skill_owner_effect_id, key, property_key, conditional, value)
{
this.id = id;
this.skill_owner_effect_id = skill_owner_effect_id;
this.key = key;
this.property_key = property_key;
this.conditional = conditional;
this.value = value;
}
static get tableName()
{
return 'skills_skill_owner_effects_conditions';
}
static get relationTypes()
{
return {
skills_skill_owner_effects: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_skill_owner_effects': 'skills_skill_owner_effects'
};
}
}
module.exports.SkillsSkillOwnerEffectsConditionsModel = SkillsSkillOwnerEffectsConditionsModel;
================================================
FILE: generated-entities/models/prisma/skills-skill-owner-effects-model.js
================================================
/**
*
* Reldens - SkillsSkillOwnerEffectsModel
*
*/
class SkillsSkillOwnerEffectsModel
{
constructor(id, skill_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty)
{
this.id = id;
this.skill_id = skill_id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.minProperty = minProperty;
this.maxProperty = maxProperty;
}
static get tableName()
{
return 'skills_skill_owner_effects';
}
static get relationTypes()
{
return {
operation_types: 'one',
skills_skill: 'one',
skills_skill_owner_effects_conditions: 'many'
};
}
static get relationMappings()
{
return {
'related_skills_skill': 'skills_skill',
'related_operation_types': 'operation_types',
'related_skills_skill_owner_effects_conditions': 'skills_skill_owner_effects_conditions'
};
}
}
module.exports.SkillsSkillOwnerEffectsModel = SkillsSkillOwnerEffectsModel;
================================================
FILE: generated-entities/models/prisma/skills-skill-physical-data-model.js
================================================
/**
*
* Reldens - SkillsSkillPhysicalDataModel
*
*/
class SkillsSkillPhysicalDataModel
{
constructor(id, skill_id, magnitude, objectWidth, objectHeight, validateTargetOnHit)
{
this.id = id;
this.skill_id = skill_id;
this.magnitude = magnitude;
this.objectWidth = objectWidth;
this.objectHeight = objectHeight;
this.validateTargetOnHit = validateTargetOnHit;
}
static get tableName()
{
return 'skills_skill_physical_data';
}
static get relationTypes()
{
return {
skills_skill: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_skill': 'skills_skill'
};
}
}
module.exports.SkillsSkillPhysicalDataModel = SkillsSkillPhysicalDataModel;
================================================
FILE: generated-entities/models/prisma/skills-skill-target-effects-conditions-model.js
================================================
/**
*
* Reldens - SkillsSkillTargetEffectsConditionsModel
*
*/
class SkillsSkillTargetEffectsConditionsModel
{
constructor(id, skill_target_effect_id, key, property_key, conditional, value)
{
this.id = id;
this.skill_target_effect_id = skill_target_effect_id;
this.key = key;
this.property_key = property_key;
this.conditional = conditional;
this.value = value;
}
static get tableName()
{
return 'skills_skill_target_effects_conditions';
}
static get relationTypes()
{
return {
skills_skill_target_effects: 'one'
};
}
static get relationMappings()
{
return {
'related_skills_skill_target_effects': 'skills_skill_target_effects'
};
}
}
module.exports.SkillsSkillTargetEffectsConditionsModel = SkillsSkillTargetEffectsConditionsModel;
================================================
FILE: generated-entities/models/prisma/skills-skill-target-effects-model.js
================================================
/**
*
* Reldens - SkillsSkillTargetEffectsModel
*
*/
class SkillsSkillTargetEffectsModel
{
constructor(id, skill_id, key, property_key, operation, value, minValue, maxValue, minProperty, maxProperty)
{
this.id = id;
this.skill_id = skill_id;
this.key = key;
this.property_key = property_key;
this.operation = operation;
this.value = value;
this.minValue = minValue;
this.maxValue = maxValue;
this.minProperty = minProperty;
this.maxProperty = maxProperty;
}
static get tableName()
{
return 'skills_skill_target_effects';
}
static get relationTypes()
{
return {
skills_skill: 'one',
operation_types: 'one',
skills_skill_target_effects_conditions: 'many'
};
}
static get relationMappings()
{
return {
'related_skills_skill': 'skills_skill',
'related_operation_types': 'operation_types',
'related_skills_skill_target_effects_conditions': 'skills_skill_target_effects_conditions'
};
}
}
module.exports.SkillsSkillTargetEffectsModel = SkillsSkillTargetEffectsModel;
================================================
FILE: generated-entities/models/prisma/skills-skill-type-model.js
================================================
/**
*
* Reldens - SkillsSkillTypeModel
*
*/
class SkillsSkillTypeModel
{
constructor(id, key)
{
this.id = id;
this.key = key;
}
static get tableName()
{
return 'skills_skill_type';
}
static get relationTypes()
{
return {
skills_skill: 'many'
};
}
static get relationMappings()
{
return {
'related_skills_skill': 'skills_skill'
};
}
}
module.exports.SkillsSkillTypeModel = SkillsSkillTypeModel;
================================================
FILE: generated-entities/models/prisma/snippets-model.js
================================================
/**
*
* Reldens - SnippetsModel
*
*/
class SnippetsModel
{
constructor(id, locale_id, key, value, created_at, updated_at)
{
this.id = id;
this.locale_id = locale_id;
this.key = key;
this.value = value;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'snippets';
}
static get relationTypes()
{
return {
locale: 'one'
};
}
static get relationMappings()
{
return {
'related_locale': 'locale'
};
}
}
module.exports.SnippetsModel = SnippetsModel;
================================================
FILE: generated-entities/models/prisma/stats-model.js
================================================
/**
*
* Reldens - StatsModel
*
*/
class StatsModel
{
constructor(id, key, label, description, base_value, customData, created_at, updated_at)
{
this.id = id;
this.key = key;
this.label = label;
this.description = description;
this.base_value = base_value;
this.customData = customData;
this.created_at = created_at;
this.updated_at = updated_at;
}
static get tableName()
{
return 'stats';
}
static get relationTypes()
{
return {
objects_stats: 'many',
players_stats: 'many'
};
}
static get relationMappings()
{
return {
'related_objects_stats': 'objects_stats',
'related_players_stats': 'players_stats'
};
}
}
module.exports.StatsModel = StatsModel;
================================================
FILE: generated-entities/models/prisma/target-options-model.js
================================================
/**
*
* Reldens - TargetOptionsModel
*
*/
class TargetOptionsModel
{
constructor(id, target_key, target_label)
{
this.id = id;
this.target_key = target_key;
this.target_label = target_label;
}
static get tableName()
{
return 'target_options';
}
static get relationTypes()
{
return {
objects_skills: 'many'
};
}
static get relationMappings()
{
return {
'related_objects_skills': 'objects_skills'
};
}
}
module.exports.TargetOptionsModel = TargetOptionsModel;
================================================
FILE: generated-entities/models/prisma/users-locale-model.js
================================================
/**
*
* Reldens - UsersLocaleModel
*
*/
class UsersLocaleModel
{
constructor(id, locale_id, user_id)
{
this.id = id;
this.locale_id = locale_id;
this.user_id = user_id;
}
static get tableName()
{
return 'users_locale';
}
static get relationTypes()
{
return {
locale: 'one',
users: 'one'
};
}
static get relationMappings()
{
return {
'related_locale': 'locale',
'related_users': 'users'
};
}
}
module.exports.UsersLocaleModel = UsersLocaleModel;
================================================
FILE: generated-entities/models/prisma/users-login-model.js
================================================
/**
*
* Reldens - UsersLoginModel
*
*/
class UsersLoginModel
{
constructor(id, user_id, login_date, logout_date)
{
this.id = id;
this.user_id = user_id;
this.login_date = login_date;
this.logout_date = logout_date;
}
static get tableName()
{
return 'users_login';
}
static get relationTypes()
{
return {
users: 'one'
};
}
static get relationMappings()
{
return {
'related_users': 'users'
};
}
}
module.exports.UsersLoginModel = UsersLoginModel;
================================================
FILE: generated-entities/models/prisma/users-model.js
================================================
/**
*
* Reldens - UsersModel
*
*/
class UsersModel
{
constructor(id, email, username, password, role_id, status, created_at, updated_at, played_time, login_count)
{
this.id = id;
this.email = email;
this.username = username;
this.password = password;
this.role_id = role_id;
this.status = status;
this.created_at = created_at;
this.updated_at = updated_at;
this.played_time = played_time;
this.login_count = login_count;
}
static get tableName()
{
return 'users';
}
static get relationTypes()
{
return {
players: 'many',
users_locale: 'many',
users_login: 'many'
};
}
static get relationMappings()
{
return {
'related_players': 'players',
'related_users_locale': 'users_locale',
'related_users_login': 'users_login'
};
}
}
module.exports.UsersModel = UsersModel;
================================================
FILE: install/css/styles.scss
================================================
/**
*
* Reldens - Installer
*
*/
$normalFont: Verdana, Geneva, sans-serif;
$reldensFont: "Play", sans-serif;
$cReldens: #2f7dde;
$cDarkBlue: #37517e;
$cWhite: #fff;
$cLightGrey: #ccc;
$cBlack: #000;
$cRed: #ff0000;
$titleBackground: #f2f2f2;
$boxBackground: rgba(255, 255, 255, 0.6);
body, html {
height: 100%;
margin: 0;
padding: 0;
}
body {
font-family: $normalFont;
background-color: transparent;
user-select: none;
.wrapper {
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 100%;
overflow: auto;
z-index: 10000;
background-size: cover;
background-color: $cDarkBlue;
.header,
.footer,
.content {
width: 100%;
}
.header,
.footer {
color: $cWhite;
}
.header {
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
background-color: $cBlack;
height: 14%;
max-height: 100px;
}
.content {
height: 80%;
}
.footer {
display: flex;
height: 6%;
background-color: $cBlack;
padding: min(10px, 1%) 0;
}
}
}
.text-black {
color: $cBlack;
}
.hidden {
display: none;
}
.header {
#your-logo {
margin-left: 2%;
}
h1 {
position: relative;
display: block;
margin: 0 auto;
padding: 2% 0;
font-family: $reldensFont;
font-size: 1.5em;
strong {
color: $cReldens;
font-weight: bold;
}
}
@media (max-height: 390px) {
h1 {
font-size: 1em;
padding-top: 0.2em;
}
}
}
.footer {
.copyright {
position: relative;
display: block;
width: 100%;
margin: 0;
padding: 0;
text-align: center;
a, a:hover, a:visited {
display: block;
color: #fff;
text-decoration: none;
padding: 0;
}
}
}
.content {
min-height: 300px;
.forms-container {
display: flex;
flex-direction: row;
height: 100%;
width: 100%;
justify-content: center;
.install-form {
display:flex;
max-width: 1000px;
flex-direction: row;
flex-wrap: wrap;
margin: 0 auto;
padding: 2%;
background-color: $boxBackground;
border: 2px solid $cWhite;
box-shadow: 10px 10px 14px 2px rgba(0, 0, 0, 0.6);
}
}
}
button,
input[type="button"],
input[type="submit"] {
cursor: pointer;
}
.row {
display: flex;
position: relative;
width: 100%;
margin: 0 auto 10px;
padding: 0;
box-sizing: border-box;
border: none;
&.hidden {
display: none;
}
}
.col-2 {
display: flex;
position: relative;
flex-direction: column;
flex-wrap: wrap;
width: 46%;
margin: 0;
padding: 2%;
}
.col-left {
align-content: end;
}
.col-right {
align-content: start;
}
@media (max-width: 725px) {
.row {
max-width: none;
}
.col-2 {
width: 90%;
padding: 0;
margin: 0 auto;
}
.footer {
display: none;
}
.content {
height: 92%;
.forms-container {
flex-direction: column;
}
}
}
@media (min-width: 725px) and (max-width: 751px) {
form .input-box.reg-re-password label {
margin-top: 1px;
}
}
h3.form-title {
text-align: center;
background: $titleBackground;
padding: 10px 0;
border: 1px solid #ccc;
}
form {
.input-box {
display: flex;
position: relative;
flex-direction: row;
justify-content: space-between;
width: 100%;
margin-bottom: 10px;
&.db-basic-config,
&.app-allow-packages-installation {
display: flex;
flex-direction: column;
background: $boxBackground;
margin-bottom: 0;
label {
width: auto;
}
input {
top: 5px;
}
.db-basic-config-checkbox,
.app-allow-packages-installation-checkbox {
display: flex;
flex-direction: row;
justify-content: end;
padding: 0 0 6px;
}
.notice-box,
.db-basic-config-notice,
.app-allow-packages-installation-notice {
margin-bottom: 0;
background: none;
}
}
&.db-sample-data,
&.app-use-https,
&.app-use-monitor,
&.app-secure-monitor,
&.mailer-enable,
&.mailer-secure,
&.firebase-enable,
&.app-admin-hot-plug {
justify-content: end;
margin-top: 4px;
padding: 0 0 6px 0;
background: $boxBackground;
label {
width: auto;
}
input {
top: 5px;
}
}
&.hidden, &.https-filter, &.monitor-filter, &.mailer-filter, &.firebase-filter {
display: none;
}
&.submit-container,
&.terms-and-conditions-link-container {
justify-content: end;
}
&.submit-container {
padding: 0 2%;
box-sizing: border-box;
justify-content: end;
align-items: start;
gap: 1rem;
}
label {
display: block;
position: relative;
font-size: 12px;
margin-top: 10px;
width: 40%;
text-align: right;
}
input {
display: block;
position: relative;
border: 1px solid $cLightGrey;
padding: 8px;
}
input[type="text"],
input[type="email"],
input[type="password"] {
width: 50%;
max-width: 268px;
}
select {
height: 2rem;
width: 100%;
max-width: 250px;
}
}
.notice-box,
.db-basic-config-notice,
.app-allow-packages-installation-notice {
display: flex;
justify-content: flex-end;
background: $boxBackground;
margin-bottom: 10px;
font-size: 12px;
font-weight: normal;
ul {
margin-bottom: 0;
}
span {
color: $cBlack;
padding: 10px;
&.danger {
font-weight: bold;
color: $cRed;
cursor: pointer;
width: 100%;
text-align: center;
ul {
display: none;
text-align: left;
&.expanded {
display: block;
}
}
}
}
}
.notice-box {
justify-content: flex-start;
padding: 0 0 1rem;
}
}
.installation-successful {
display: block;
padding: 10px;
background: $boxBackground;
text-align: center;
text-decoration: none;
font-size: 2em;
color: #000;
}
.app-error, .db-error {
p {
display: none;
width: 100%;
padding: 2%;
background: $boxBackground;
font-size: 12px;
font-weight: bold;
color: $cRed;
}
p.active {
display: block;
}
}
.loading-status-wrapper {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
&.hidden {
display: none;
}
}
.install-loading {
max-width: 50px;
}
.install-status-message {
padding: 8px 12px;
font-size: 11px;
font-weight: bold;
color: $cDarkBlue;
background: $boxBackground;
border-radius: 3px;
text-align: center;
white-space: nowrap;
}
#install-submit-button.disabled {
background-color: $boxBackground;
}
================================================
FILE: install/index.html
================================================
DwDeveloper - Reldens | MMORPG Platform
================================================
FILE: install/index.js
================================================
/**
*
* Reldens - Install
*
*/
const DB_CLIENTS_MAP = {
'prisma': [
{value: 'mysql', label: 'MySQL'},
{value: 'postgresql', label: 'PostgreSQL (manual)'},
{value: 'sqlite', label: 'SQLite (manual)'},
{value: 'sqlserver', label: 'SQL Server (manual)'},
{value: 'mongodb', label: 'MongoDB (manual)'},
{value: 'cockroachdb', label: 'CockroachDB (manual)'}
],
'objection-js': [
{value: 'mysql', label: 'MySQL (native)'},
{value: 'mysql2', label: 'MySQL2 (recommended)'},
{value: 'pg', label: 'PostgreSQL (manual)'},
{value: 'sqlite3', label: 'SQLite3 (manual)'},
{value: 'better-sqlite3', label: 'Better-SQLite3 (manual)'},
{value: 'mssql', label: 'SQL Server (manual)'},
{value: 'oracledb', label: 'Oracle DB (manual)'},
{value: 'cockroachdb', label: 'CockroachDB (manual)'}
],
'mikro-orm': [
{value: 'mysql', label: 'MySQL'},
{value: 'mariadb', label: 'MariaDB (manual)'},
{value: 'postgresql', label: 'PostgreSQL (manual)'},
{value: 'sqlite', label: 'SQLite (manual)'},
{value: 'mongodb', label: 'MongoDB (manual)'},
{value: 'mssql', label: 'SQL Server (manual)'},
{value: 'better-sqlite3', label: 'Better-SQLite3 (manual)'}
]
};
window.addEventListener('load', () => {
const expanders = [
{key: 'app-use-https', filterClass: 'https-filter'},
{key: 'app-use-monitor', filterClass: 'monitor-filter'},
{key: 'app-secure-monitor', filterClass: 'secure-monitor-filter'},
{key: 'mailer-enable', filterClass: 'mailer-filter'},
{key: 'firebase-enable', filterClass: 'firebase-filter'}
];
function toggleExpander(isChecked, expander)
{
const display = isChecked ? 'flex' : 'none';
const elements = document.getElementsByClassName(expander.filterClass);
for (let element of elements) {
element.style.display = display;
}
}
for(let expander of expanders){
let expanderElement = document.getElementById(expander.key);
expanderElement.addEventListener('click', (event) => {
toggleExpander(event?.currentTarget?.checked, expander);
if('app-use-monitor' === expander.key && event?.currentTarget?.checked){
let secureMonitorElement = document.getElementById('app-secure-monitor');
if(secureMonitorElement){
toggleExpander(secureMonitorElement.checked, {key: 'app-secure-monitor', filterClass: 'secure-monitor-filter'});
}
}
});
toggleExpander(expanderElement.checked, expander);
}
let useMonitorElement = document.getElementById('app-use-monitor');
let secureMonitorElement = document.getElementById('app-secure-monitor');
if(useMonitorElement?.checked && secureMonitorElement){
toggleExpander(secureMonitorElement.checked, {key: 'app-secure-monitor', filterClass: 'secure-monitor-filter'});
}
let dangerSpans = document.querySelectorAll('span.danger');
for(let dangerSpan of dangerSpans){
dangerSpan.addEventListener('click', () => {
let ulElement = dangerSpan.querySelector('ul');
if(ulElement){
ulElement.classList.toggle('expanded');
}
});
}
let urlParams = new URL(window.location.href).searchParams;
if('1' === urlParams.get('success')){
document.querySelector('.forms-container').style.display = 'none';
let newLink = document.createElement('a');
newLink.href = '/?ready=1';
newLink.target = '_blank';
newLink.innerHTML = 'Installation successful, click here to open your game!';
newLink.classList.add('installation-successful');
document.querySelector('.content').append(newLink);
}
function showInstallError(code)
{
let errorElement = document.querySelector('.'+code);
if(!errorElement){
let responseError = document.querySelector('.response-error');
if(responseError){
responseError.textContent = 'Installation failed with error: '+code;
}
return;
}
errorElement.classList.add('active');
}
let errorCode = (urlParams.get('error') || '').toString();
if('' !== errorCode){
showInstallError(errorCode);
}
function updateClientOptions(driverValue, currentClient)
{
let clientSelect = document.getElementById('db-client');
if(!clientSelect){
return;
}
clientSelect.innerHTML = '';
let clients = DB_CLIENTS_MAP[driverValue] || DB_CLIENTS_MAP['prisma'];
for(let client of clients){
let option = document.createElement('option');
option.value = client.value;
option.text = client.label;
if(currentClient && client.value === currentClient){
option.selected = true;
}
clientSelect.appendChild(option);
}
if(!currentClient){
let defaultClient = 'objection-js' === driverValue ? 'mysql2' : 'mysql';
for(let i = 0; i < clientSelect.options.length; i++){
if(clientSelect.options[i].value === defaultClient){
clientSelect.selectedIndex = i;
break;
}
}
}
}
let storageDriverSelect = document.getElementById('db-storage-driver');
if(storageDriverSelect){
let currentClient = document.getElementById('db-client')?.value || '';
updateClientOptions(storageDriverSelect.value, currentClient);
storageDriverSelect.addEventListener('change', (event) => {
updateClientOptions(event.target.value, null);
});
}
let statusIntervalId = null;
let lastStatusMessage = '';
function startStatusPolling()
{
if(statusIntervalId){
return;
}
statusIntervalId = setInterval(() => {
fetch('/install-status.json?t='+Date.now())
.then(response => {
if(!response.ok){
return null;
}
return response.json();
})
.then(data => {
if(!data || !data.message){
return;
}
if(data.message === lastStatusMessage){
return;
}
lastStatusMessage = data.message;
let statusContainer = document.querySelector('.install-status-message');
if(statusContainer){
statusContainer.textContent = data.message;
}
})
.catch(() => {
});
}, 2000);
}
document.getElementById('install-form').addEventListener('submit', () => {
let loadingWrapper = document.querySelector('.loading-status-wrapper');
if(loadingWrapper){
loadingWrapper.classList.remove('hidden');
}
let installButton = document.getElementById('install-submit-button');
if(installButton){
installButton.classList.add('disabled');
installButton.disabled = true;
}
startStatusPolling();
});
});
================================================
FILE: install/site.webmanifest
================================================
{"name":"","short_name":"","icons":[{"src":"./assets/favicons/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"./assets/favicons/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
================================================
FILE: lib/actions/client/game-manager-enricher.js
================================================
/**
*
* Reldens - GameManagerEnricher
*
* Enriches the GameManager with a ReceiverWrapper instance for skill message processing.
*
*/
const { ReceiverWrapper } = require('./receiver-wrapper');
const { Logger } = require('@reldens/utils');
/**
* @typedef {import('../../users/client/player-engine').PlayerEngine} PlayerEngine
* @typedef {import('../../game/client/game-manager').GameManager} GameManager - CUSTOM DYNAMIC
* @typedef {import('../../game/client/room-events').RoomEvents} RoomEvents
*/
class GameManagerEnricher
{
/**
* @param {PlayerEngine} player
* @param {RoomEvents} roomEvents
* @param {GameManager} gameManager
* @returns {boolean|void}
*/
static withReceiver(player, roomEvents, gameManager)
{
if(!player || !roomEvents || !gameManager){
Logger.error('Invalid input parameters for GameManagerEnricher.withReceiver method.');
return false;
}
if(player?.playerId !== roomEvents?.room.sessionId){
return false;
}
if(!gameManager.skills){
gameManager.skills = new ReceiverWrapper({owner: player, roomEvents, events: gameManager.events});
}
if(!gameManager.skillsQueue?.length){
return false;
}
for(let message of gameManager.skillsQueue){
gameManager.skills.processMessage(message);
}
gameManager.skillsQueue = [];
}
}
module.exports.GameManagerEnricher = GameManagerEnricher;
================================================
FILE: lib/actions/client/messages-guard.js
================================================
/**
*
* Reldens - MessagesGuard
*
* Validates messages to ensure they contain valid action identifiers.
*
*/
const { SkillConst } = require('@reldens/skills');
const { ActionsConst } = require('../constants');
/**
* @typedef {Object} ActionMessage
* @property {string} [act]
*/
class MessagesGuard
{
/**
* @param {ActionMessage} message
* @returns {boolean}
*/
static validate(message)
{
if(!message.act){
return false;
}
return (
0 === message.act.indexOf(SkillConst.ACTIONS_PREF)
|| -1 !== message.act.indexOf(ActionsConst.ACTIONS.SUFFIX.ATTACK)
|| -1 !== message.act.indexOf(ActionsConst.ACTIONS.SUFFIX.EFFECT)
|| -1 !== message.act.indexOf(ActionsConst.ACTIONS.SUFFIX.HIT)
);
}
}
module.exports.MessagesGuard = MessagesGuard;
================================================
FILE: lib/actions/client/messages-handler.js
================================================
/**
*
* Reldens - MessagesHandler
*
* Processes or queues action messages based on the current game state.
*
*/
const { MessagesGuard } = require('./messages-guard');
const { sc } = require('@reldens/utils');
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
*
* @typedef {Object} ActionMessage
* @property {string} act
*/
class MessagesHandler
{
/**
* @param {ActionMessage} message
* @param {GameManager} gameManager
* @returns {boolean|void}
*/
static processOrQueueMessage(message, gameManager)
{
if(!MessagesGuard.validate(message)){
return false;
}
let currentScene = gameManager.getActiveScene();
if(currentScene && currentScene.player){
return gameManager.skills.processMessage(message);
}
if(!sc.hasOwn(gameManager, 'skillsQueue')){
gameManager.skillsQueue = [];
}
gameManager.skillsQueue.push(message);
}
}
module.exports.MessagesHandler = MessagesHandler;
================================================
FILE: lib/actions/client/player-selector.js
================================================
/**
*
* Reldens - PlayerSelector.
*
* Manages the class path selector for player creation.
*
*/
const { ActionsConst } = require('../constants');
const { GameConst } = require('../../game/constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('../../game/client/game-dom').GameDom} GameDom
*
* @typedef {Object} PlayerSelectorProps
* @property {GameManager} [gameManager]
* @property {EventsManager} [events]
*/
class PlayerSelector
{
/**
* @param {PlayerSelectorProps} props
*/
constructor(props)
{
/** @type {GameManager|false} */
this.gameManager = sc.get(props, 'gameManager', false);
if(!this.gameManager){
Logger.error('Game Manager undefined in ActionsPlugin PlayerSelector.');
}
/** @type {EventsManager|false} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in ActionsPlugin PlayerSelector.');
}
/** @type {GameDom} */
this.gameDom = this.gameManager.gameDom;
}
/**
* @param {Object} classesData
* @param {Object} playersConfig
* @param {Object} activePlayer
* @returns {boolean}
*/
populateClassesSelector(classesData, playersConfig, activePlayer)
{
if(!sc.isObject(classesData) || 0 === Object.keys(classesData).length){
Logger.error('Classes not defined, can not populate the classes selector.');
this.gameDom.getElement('.player-selection-form-errors')?.append(
this.gameManager.services?.translator.t('game.errors.missingClasses')
);
return false;
}
let multiConfig = sc.get(playersConfig, 'multiplePlayers', false);
if((!multiConfig || !multiConfig.enabled) && activePlayer){
return false;
}
let playerAdditional = this.gameDom.getElement(ActionsConst.SELECTORS.PLAYER_CREATION_ADDITIONAL_INFO);
if(!playerAdditional){
return false;
}
// @TODO - BETA - Extract all the DOM objects creation, move into the gameDom.
this.gameDom.getElement(ActionsConst.SELECTORS.PLAYER_CREATE_FORM)?.classList.remove('hidden');
let div = this.gameDom.createElement('div');
div.id = 'class-path-selector-box';
div.classList.add('input-box');
let label = this.gameDom.createElement('label');
let classPathSelectId = 'class-path-select';
label.htmlFor = classPathSelectId;
label.innerText = this.gameManager.services.translator.t(ActionsConst.SNIPPETS.SELECT_CLASS_PATH);
let select = this.gameDom.createElement('select');
select.id = classPathSelectId;
select.name = 'class_path_select';
for(let id of Object.keys(classesData)){
let option = new Option(classesData[id].label, id);
option.dataset.key = classesData[id].key;
select.append(option);
}
div.append(label);
div.append(select);
if(this.gameManager.config.getWithoutLogs('client/players/multiplePlayers/showAvatar', true)){
let avatarDiv = this.gameDom.createElement('div');
avatarDiv.className = 'avatar-container';
this.appendAvatarOnSelector(select, avatarDiv, playersConfig);
div.append(avatarDiv);
}
playerAdditional.append(div);
return true;
}
/**
* @param {HTMLSelectElement} select
* @param {HTMLElement} container
* @param {Object} playersConfig
*/
appendAvatarOnSelector(select, container, playersConfig)
{
// @TODO - BETA - Refactor, extract all the styles and replace the avatar background by an element.
let avatar = this.gameDom.createElement('div');
let avatarKey = select.options[select.selectedIndex].dataset.key;
avatar.classList.add('class-path-select-avatar');
avatar.style.backgroundImage = `url('/assets/custom/sprites/${avatarKey}${GameConst.FILES.EXTENSIONS.PNG}')`;
let widthInPx = playersConfig.size.width+'px';
avatar.style.backgroundPositionX = '-'+widthInPx;
avatar.style.width = widthInPx;
avatar.style.height = playersConfig.size.height+'px';
select.addEventListener('change', () => {
let avatarKey = select.options[select.selectedIndex].dataset.key;
avatar.style.backgroundImage = `url('/assets/custom/sprites/${avatarKey}${GameConst.FILES.EXTENSIONS.PNG}')`;
});
container.append(avatar);
}
}
module.exports.PlayerSelector = PlayerSelector;
================================================
FILE: lib/actions/client/plugin.js
================================================
/**
*
* Reldens - ActionsPlugin
*
* Client-side plugin for managing actions, skills, and class paths.
*
*/
const { SkillsUi } = require('./skills-ui');
const { PluginInterface } = require('../../features/plugin-interface');
const { PlayerSelector } = require('./player-selector');
const { PreloaderHandler } = require('./preloader-handler');
const { MessagesHandler } = require('./messages-handler');
const { GameManagerEnricher } = require('./game-manager-enricher');
const Translations = require('./snippets/en_US');
const { TranslationsMapper } = require('../../snippets/client/translations-mapper');
const { ActionsConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
* @typedef {import('@reldens/utils').EventsManager} EventsManager
*/
class ActionsPlugin extends PluginInterface
{
/**
* @param {Object} props
*/
setup(props)
{
this.gameManager = sc.get(props, 'gameManager', false);
if(!this.gameManager){
Logger.error('Game Manager undefined in ActionsPlugin.');
}
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in ActionsPlugin.');
}
this.playerSelector = new PlayerSelector(props);
this.preloaderHandler = new PreloaderHandler(props);
this.setTranslations();
this.listenEvents();
}
/**
* @returns {boolean|void}
*/
setTranslations()
{
if(!this.gameManager){
return false;
}
TranslationsMapper.forConfig(this.gameManager.config.client, Translations, ActionsConst.MESSAGE.DATA_VALUES);
}
/**
* @returns {boolean|void}
*/
listenEvents()
{
if(!this.events || !this.gameManager){
return false;
}
this.events.on('reldens.preloadUiScene', (uiScene) => {
this.preloaderHandler.loadContents(uiScene);
});
this.events.on('reldens.createPreload', (preloadScene) => {
this.preloaderHandler.createAnimations(preloadScene);
});
this.events.on('reldens.createUiScene', (preloadScene) => {
this.uiManager = new SkillsUi(preloadScene);
this.uiManager.createUi();
});
this.events.on('reldens.beforeCreateEngine', (initialGameData) => {
this.playerSelector.populateClassesSelector(
sc.get(initialGameData, 'classesData', {}),
initialGameData.gameConfig.client.players,
initialGameData.player
);
});
this.events.on('reldens.activateRoom', (room) => {
room.onMessage('*', (message) => {
MessagesHandler.processOrQueueMessage(message, this.gameManager);
});
});
this.events.on('reldens.playersOnAddReady', (props) => {
GameManagerEnricher.withReceiver(props.player, props.roomEvents, this.gameManager);
});
}
}
module.exports.ActionsPlugin = ActionsPlugin;
================================================
FILE: lib/actions/client/preloader-handler.js
================================================
/**
*
* Reldens - PreloaderHandler.
*
* Manages preloading and creation of animations for skills and class paths.
*
*/
const { GameConst } = require('../../game/constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('phaser').Scene} PhaserScene
*
* @typedef {Object} PreloaderHandlerProps
* @property {GameManager} [gameManager]
* @property {EventsManager} [events]
* @property {string} [assetsCustomActionsSpritesPath]
*
* @typedef {Object} AnimationData
* @property {string} key
* @property {string} [skillKey]
* @property {Object} animationData
* @property {Object} [classKey]
*/
class PreloaderHandler
{
/**
* @param {PreloaderHandlerProps} props
*/
constructor(props)
{
/** @type {GameManager|false} */
this.gameManager = sc.get(props, 'gameManager', false);
if(!this.gameManager){
Logger.error('Game Manager undefined in ActionsPlugin PreloaderHandler.');
}
/** @type {EventsManager|false} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in ActionsPlugin PreloaderHandler.');
}
this.setProperties(props);
}
/**
* @param {PreloaderHandlerProps} props
* @returns {boolean|void}
*/
setProperties(props)
{
if(!this.gameManager){
return false;
}
this.gameDom = this.gameManager.gameDom;
this.initialGameData = this.gameManager.initialGameData;
this.levelsAnimConfig = this.gameManager.config.get('client/levels/animations');
this.skillsAnimConfig = this.gameManager.config.get('client/skills/animations');
this.assetsCustomActionsSpritesPath = sc.get(
props,
'assetsCustomActionsSpritesPath',
'assets/custom/actions/sprites/'
);
if(!this.gameManager.loadedAssets){
this.gameManager.loadedAssets = {};
}
if(!this.gameManager.createdAnimations){
this.gameManager.createdAnimations = {};
}
}
/**
* @param {PhaserScene} uiScene
*/
loadContents(uiScene)
{
uiScene.load.html('skillsClassPath', '/assets/features/skills/templates/ui-class-path.html');
uiScene.load.html('skillsLevel', '/assets/features/skills/templates/ui-level.html');
uiScene.load.html('skillsExperience', '/assets/features/skills/templates/ui-experience.html');
uiScene.load.html('skills', '/assets/features/skills/templates/ui-skills.html');
uiScene.load.html('skillBox', '/assets/features/skills/templates/ui-skill-box.html');
uiScene.load.html('actionBox', '/assets/html/ui-action-box.html');
this.preloadClassPaths(uiScene);
this.loopAnimationsAnd(this.levelsAnimConfig, 'preload', uiScene);
this.loopAnimationsAnd(this.skillsAnimConfig, 'preload', uiScene);
}
/**
* @param {PhaserScene} uiScene
* @returns {boolean|void}
*/
preloadClassPaths(uiScene)
{
let classesData = sc.get(this.initialGameData, 'classesData', false);
if(!classesData){
return false;
}
for(let i of Object.keys(classesData)){
let avatarKey = classesData[i].key;
let spriteSheetLoader = uiScene.load.spritesheet(
avatarKey,
'/assets/custom/sprites/'+avatarKey+GameConst.FILES.EXTENSIONS.PNG,
uiScene.playerSpriteSize
);
spriteSheetLoader.on('filecomplete', async (completedKey) => {
this.gameManager.loadedAssets[completedKey] = completedKey;
});
}
}
/**
* @param {PhaserScene} preloadScene
*/
createAnimations(preloadScene)
{
let levelsAnimations = this.levelsAnimConfig;
this.loopAnimationsAnd(levelsAnimations, 'create', preloadScene);
let skillsAnimations = this.skillsAnimConfig;
this.loopAnimationsAnd(skillsAnimations, 'create', preloadScene);
this.createAvatarsAnimations(preloadScene);
}
/**
* @param {PhaserScene} preloadScene
* @returns {Object|boolean}
*/
createAvatarsAnimations(preloadScene)
{
let classesData = sc.get(this.initialGameData, 'classesData', false);
if(!classesData){
Logger.debug('Classes data not found. Fallback to player avatar.');
return false;
}
if(!this.gameManager.mappedAvatars){
this.gameManager.mappedAvatars = {};
}
Logger.debug({availableClassesData: classesData});
for(let i of Object.keys(classesData)){
let avatarKey = classesData[i].key;
if(!this.gameManager.loadedAssets[avatarKey]){
avatarKey = GameConst.IMAGE_PLAYER;
Logger.info('Avatar for class path "'+avatarKey+'" not found in assets. Fallback to player avatar.');
}
this.gameManager.mappedAvatars[avatarKey] = avatarKey;
preloadScene.createPlayerAnimations(avatarKey);
}
return this.gameManager.mappedAvatars;
}
/**
* @param {Object} animations
* @param {string} command
* @param {PhaserScene} uiScene
* @returns {boolean|void}
*/
loopAnimationsAnd(animations, command, uiScene)
{
if(!animations){
Logger.warning('Animations not found.', animations);
return false;
}
for(let i of Object.keys(animations)){
let data = animations[i];
if(!data.animationData.enabled){
Logger.debug('Animation "'+i+'" not enabled, skipping.', data);
continue;
}
Logger.debug({[command+'Animation']: data});
this[command+'Animation'](data, uiScene);
}
}
/**
* @param {AnimationData} data
* @param {PhaserScene} uiScene
*/
preloadAnimation(data, uiScene)
{
// @TODO - BETA - Remove the hardcoded file extensions.
// @NOTE: here we use have two keys, the animation key and the animationData.img, this is because we could have
// a single sprite with multiple attacks, and use the start and end frame to run the required one.
if(
sc.hasOwn(data.animationData, ['type', 'img'])
&& GameConst.ANIMATIONS_TYPE.SPRITESHEET === data.animationData.type
){
this.preloadAnimationsInDirections(data, uiScene);
}
if(data.classKey && sc.isFunction(data.classKey['prepareAnimation'])){
data.classKey['prepareAnimation']({data, uiScene, pack: this});
}
}
/**
* @param {AnimationData} data
* @param {PhaserScene} uiScene
*/
preloadAnimationsInDirections(data, uiScene)
{
// try load directions:
// - 1: both (this is to include diagonals)
// - 2: up/down
// - 3: left/right
let animDir = sc.get(data.animationData, 'dir', 0);
if(0 === animDir){
uiScene.load.spritesheet(
this.getAnimationKey(data),
this.assetsCustomActionsSpritesPath + data.animationData.img+GameConst.FILES.EXTENSIONS.PNG,
data.animationData
);
return;
}
// @TODO - BETA - Refactor and implement animDir = 1 (both): up_right, up_left, down_right, down_left.
if(1 === animDir || 2 === animDir){
this.preloadSpriteInDirection(uiScene, data, GameConst.UP);
this.preloadSpriteInDirection(uiScene, data, GameConst.DOWN);
}
if(1 === animDir || 3 === animDir){
this.preloadSpriteInDirection(uiScene, data, GameConst.LEFT);
this.preloadSpriteInDirection(uiScene, data, GameConst.RIGHT);
}
}
/**
* @param {PhaserScene} uiScene
* @param {AnimationData} data
* @param {string} direction
*/
preloadSpriteInDirection(uiScene, data, direction)
{
uiScene.load.spritesheet(
this.getAnimationKey(data, direction),
this.assetsCustomActionsSpritesPath+data.animationData.img+'_'+direction+GameConst.FILES.EXTENSIONS.PNG,
data.animationData
);
}
/**
* @param {AnimationData} data
* @param {PhaserScene} uiScene
*/
createAnimation(data, uiScene)
{
if(
sc.hasOwn(data.animationData, ['type', 'img'])
&& data.animationData.type === GameConst.ANIMATIONS_TYPE.SPRITESHEET
){
let animDir = sc.get(data.animationData, 'dir', 0);
0 < animDir
? this.createWithMultipleDirections(uiScene, data, animDir)
: this.createWithDirection(data, uiScene);
}
if(data.classKey && sc.isFunction(data.classKey['createAnimation'])){
data.classKey['createAnimation']({data, uiScene, pack: this});
}
}
/**
* @param {PhaserScene} uiScene
* @param {AnimationData} data
* @param {number} animDir
*/
createWithMultipleDirections(uiScene, data, animDir)
{
// @TODO - BETA - Refactor and implement animDir = 1 (both): up_right, up_left, down_right,
// down_left.
uiScene.directionalAnimations[this.getAnimationKey(data)] = data.animationData.dir;
if(1 === animDir || 2 === animDir){
this.createWithDirection(data, uiScene, GameConst.UP);
this.createWithDirection(data, uiScene, GameConst.DOWN);
}
if(1 === animDir || 3 === animDir){
this.createWithDirection(data, uiScene, GameConst.LEFT);
this.createWithDirection(data, uiScene, GameConst.RIGHT);
}
}
/**
* @param {AnimationData} data
* @param {PhaserScene} uiScene
* @param {string} [direction]
* @returns {Object}
*/
createWithDirection(data, uiScene, direction = '')
{
let animationCreateData = this.prepareAnimationData(data, uiScene, direction);
if(this.gameManager.createdAnimations[animationCreateData.key]){
return this.gameManager.createdAnimations[animationCreateData.key];
}
let newAnimation = uiScene.anims.create(animationCreateData);
if(sc.hasOwn(data.animationData, 'destroyTime')){
newAnimation.destroyTime = data.animationData.destroyTime;
}
if(sc.hasOwn(data.animationData, 'depthByPlayer')){
newAnimation.depthByPlayer = data.animationData.depthByPlayer;
}
this.gameManager.createdAnimations[animationCreateData.key] = newAnimation;
return this.gameManager.createdAnimations[animationCreateData.key];
}
/**
* @param {AnimationData} data
* @param {PhaserScene} uiScene
* @param {string} [direction]
* @returns {Object}
*/
prepareAnimationData(data, uiScene, direction = '')
{
// @NOTE: here we use have two keys, the animation key and the animationData.img, this is because we could have
// a single sprite with multiple attacks, and use the start and end frame to run the required one.
let imageKey = this.getAnimationKey(data, direction);
let animationCreateData = {
key: imageKey,
frames: uiScene.anims.generateFrameNumbers(imageKey, data.animationData),
hideOnComplete: sc.get(data.animationData, 'hide', true),
};
if(sc.hasOwn(data.animationData, 'duration')){
animationCreateData.duration = data.animationData.duration;
} else {
animationCreateData.frameRate = sc.get(data.animationData, 'frameRate', uiScene.configuredFrameRate);
}
if(sc.hasOwn(data.animationData, 'repeat')){
animationCreateData.repeat = data.animationData.repeat;
}
return animationCreateData;
}
/**
* @param {AnimationData} data
* @param {string} [direction]
* @returns {string}
*/
getAnimationKey(data, direction = '')
{
return (data.skillKey ? data.skillKey+'_' : '')+data.key+(direction && '' !== direction ? '_'+direction : '');
}
}
module.exports.PreloaderHandler = PreloaderHandler;
================================================
FILE: lib/actions/client/receiver-wrapper.js
================================================
/**
*
* Reldens - ReceiverWrapper
*
* Handles skill messages and plays animations for attacks, effects, and level progression.
*
*/
const { GameConst } = require('../../game/constants');
const { ActionsConst } = require('../constants');
const { Receiver } = require('@reldens/skills');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@colyseus/core').Room} ColyseusRoom
* @typedef {import('phaser').Scene} PhaserScene
*
* @typedef {Object} ReceiverWrapperProps
* @property {EventsManager} [events]
* @property {Object} [roomEvents]
* @property {GameManager} roomEvents.gameManager
* @property {ColyseusRoom} roomEvents.room
*
* @typedef {Object} SkillMessage
* @property {string} act
* @property {number} [x]
* @property {number} [y]
* @property {string} [owner]
* @property {string} [target]
* @property {Object} [data]
*/
class ReceiverWrapper extends Receiver
{
/**
* @param {ReceiverWrapperProps} props
*/
constructor(props)
{
super(props);
/** @type {EventsManager|false} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in ReceiverWrapper.');
}
/** @type {GameManager|false} */
this.gameManager = sc.get(props.roomEvents, 'gameManager', false);
if(!this.gameManager){
Logger.error('Game Manager undefined in ReceiverWrapper.');
}
// @TODO - BETA - Extract and replace Colyseus Room instances by a new communications room driver.
/** @type {ColyseusRoom|false} */
this.room = sc.get(props.roomEvents, 'room', false);
if(!this.room){
Logger.error('Room undefined in ReceiverWrapper.');
}
/** @type {Object|undefined} */
this.translator = this.gameManager?.services?.translator;
}
/**
* @param {SkillMessage} message
* @returns {boolean|void}
*/
processMessage(message)
{
if(!this.gameManager){
return false;
}
let currentScene = this.gameManager.getActiveScene();
if(!currentScene || !currentScene.player){
return false;
}
super.processMessage(message);
this.playAttackOrEffectAnimation(message, currentScene);
this.playHitAnimation(message, currentScene);
}
/**
* @param {SkillMessage} message
* @param {PhaserScene} currentScene
*/
playHitAnimation(message, currentScene)
{
if(-1 === message.act.indexOf('_hit')){
return;
}
this.runHitAnimation(message.x, message.y, currentScene, message.act);
}
/**
* @param {SkillMessage} message
* @param {PhaserScene} currentScene
*/
playAttackOrEffectAnimation(message, currentScene)
{
let isEffect = -1 !== message.act.indexOf('_eff');
let isAttack = -1 !== message.act.indexOf('_atk');
if(!isAttack && !isEffect){
return;
}
this.events.emitSync('reldens.playerAttack', message, this.room);
let actKey = isEffect ? '_eff' : '_atk';
let animKey = message.act.substring(0, message.act.indexOf(actKey));
let {ownerSprite, targetSprite, targetType} = this.extractOwnerTargetAndType(currentScene, message);
// @TODO - BETA - Refactor to use a single play animation method and make sure the animation is valid.
let actAnimKey = sc.get(this.gameManager.config.client.skills.animations, animKey, 'default' + actKey);
if(ownerSprite && currentScene.getAnimationByKey(actAnimKey)){
let ownerAnim = currentScene.physics.add.sprite(ownerSprite.x, ownerSprite.y, actAnimKey);
ownerAnim.setDepth(200000);
// @TODO - BETA - Refactor and implement animDir = 1 (both): up_right, up_left, down_right,
// down_left.
let playDir = '';
if(sc.hasOwn(this.gameManager.gameEngine.uiScene.directionalAnimations, actAnimKey)){
let animDir = this.gameManager.gameEngine.uiScene.directionalAnimations[actAnimKey];
playDir = (animDir === 3) ?
(ownerSprite.x < targetSprite.x ? '_right' : '_left')
: (ownerSprite.y < targetSprite.y ? '_down' : '_up');
}
ownerAnim.anims.play(actAnimKey + playDir, true).on('animationcomplete', () => {
ownerAnim.destroy();
});
}
if(targetSprite){
this.runHitAnimation(
targetSprite.x,
targetSprite.y,
currentScene,
animKey + '_hit',
message.target,
targetType
);
}
}
/**
* @param {PhaserScene} currentScene
* @param {SkillMessage} message
* @returns {Object|boolean}
*/
extractOwnerTargetAndType(currentScene, message)
{
if(!currentScene){
Logger.critical('Current scene not found.', currentScene, message);
return false;
}
let ownerSprite = false;
let targetSprite = false;
let targetType = ActionsConst.DATA_TYPE_VALUE_PLAYER;
let playersList = currentScene.player.players;
let objectsList = currentScene.objectsAnimations;
let isPvP = (sc.hasOwn(playersList, message.owner) && sc.hasOwn(playersList, message.target));
if(isPvP){
ownerSprite = playersList[message.owner];
targetSprite = playersList[message.target];
return {ownerSprite, targetSprite, targetType};
}
if(sc.hasOwn(objectsList, message.owner)){
ownerSprite = objectsList[message.owner].sceneSprite;
targetSprite = playersList[message.target];
}
if(sc.hasOwn(objectsList, message.target)){
targetSprite = objectsList[message.target].sceneSprite;
ownerSprite = playersList[message.owner];
targetType = ActionsConst.DATA_TYPE_VALUE_OBJECT;
}
return {ownerSprite, targetSprite, targetType};
}
/**
* @param {number} x
* @param {number} y
* @param {PhaserScene} currentScene
* @param {string} hitKey
* @param {string} [targetKey]
* @param {string} [targetType]
* @returns {boolean|void}
*/
runHitAnimation(x, y, currentScene, hitKey, targetKey, targetType)
{
// @TODO - BETA - Refactor.
let allAnimations = this.gameManager.config.client.skills.animations;
let hitAnimKey = sc.hasOwn(allAnimations, hitKey) ? hitKey : ActionsConst.DEFAULT_HIT_ANIMATION_KEY;
if(!currentScene.getAnimationByKey(hitAnimKey) || !sc.hasOwn(allAnimations, hitAnimKey)){
return false;
}
let targetSprite = false;
let targetSpriteId = false;
if(targetType === ActionsConst.DATA_TYPE_VALUE_PLAYER){
targetSprite = this.gameManager.getCurrentPlayer().players[targetKey];
targetSpriteId = targetSprite.playerId;
}
if(targetType === ActionsConst.DATA_TYPE_VALUE_OBJECT){
targetSprite = currentScene.objectsAnimations[targetKey];
targetSpriteId = targetKey;
}
let hitSprite = currentScene.physics.add.sprite(x, y, hitAnimKey);
hitSprite = this.setTargetSpriteDepth(targetSprite, hitAnimKey, targetSpriteId, hitSprite, allAnimations);
hitSprite.anims.play(hitAnimKey, true).on('animationcomplete', () => {
hitSprite.destroy();
if(targetSprite && sc.hasOwn(targetSprite, 'moveSprites')){
delete targetSprite.moveSprites[hitAnimKey+'_'+targetSpriteId];
}
});
}
/**
* @param {Object} targetSprite
* @param {string} hitAnimKey
* @param {string|number} targetSpriteId
* @param {Object} hitSprite
* @param {Object} allAnimations
* @returns {Object}
*/
setTargetSpriteDepth(targetSprite, hitAnimKey, targetSpriteId, hitSprite, allAnimations)
{
if(!targetSprite){
hitSprite.setDepth(300000);
return hitSprite;
}
if(sc.hasOwn(targetSprite, 'targetSprite')){
targetSprite.moveSprites[hitAnimKey + '_' + targetSpriteId] = hitSprite;
}
let animData = allAnimations[hitAnimKey];
let depth = targetSprite.depth+('above' === sc.get(animData.animationData, 'depthByPlayer', '') ? 100 : -0.1);
hitSprite.depthByPlayer = animData.animationData.depthByPlayer;
hitSprite.setDepth(depth);
return hitSprite;
}
/**
* @param {SkillMessage} message
*/
updateLevelAndExperience(message)
{
this.gameManager.gameDom.updateContent(
ActionsConst.SELECTORS.LEVEL_LABEL,
this.translator.t(
ActionsConst.SNIPPETS.LEVEL,
{currentLevel: message.data[ActionsConst.MESSAGE.DATA.LEVEL]}
)
);
this.onLevelExperienceAdded(message);
let classPathLabel = message.data[ActionsConst.MESSAGE.DATA.CLASS_PATH_LABEL];
if(classPathLabel){
this.gameManager.gameDom.updateContent(
ActionsConst.SELECTORS.CLASS_PATH_LABEL,
this.translator.t(ActionsConst.SNIPPETS.CLASS_PATH_LABEL, {classPathLabel})
);
}
let nextLevelExperience = message.data[ActionsConst.MESSAGE.DATA.NEXT_LEVEL_EXPERIENCE];
if(nextLevelExperience){
this.gameManager.gameDom.updateContent(
ActionsConst.SELECTORS.NEXT_LEVEL_EXPERIENCE,
this.translator.t(ActionsConst.SNIPPETS.NEXT_LEVEL_EXPERIENCE, {nextLevelExperience})
);
}
}
/**
* @param {SkillMessage} message
* @returns {boolean|void}
*/
onInitClassPathEnd(message)
{
// @NOTE: careful with messages received and double ui elements generation.
if(this.gameManager.skills && this.gameManager.skills.uiCreated){
return false;
}
this.gameManager.skills.uiCreated = true;
this.updateLevelAndExperience(message);
this.gameManager.skills.skills = message.data[ActionsConst.MESSAGE.DATA.SKILL_LEVEL];
this.gameManager.getFeature('actions').uiManager.appendSkills(message.data.skl);
}
/**
* @param {SkillMessage} message
*/
onLevelUp(message)
{
this.updateLevelAndExperience(message);
if(sc.hasOwn(message.data, 'skl')){
Object.assign(this.gameManager.skills.skills, message.data.skl);
this.gameManager.getFeature('actions').uiManager.appendSkills(message.data.skl);
}
let levelUpAnimKey = this.getLevelUpAnimationKey(message.data.skl);
if(levelUpAnimKey){
this.playSkillPlayerAnimation(this.gameManager.getCurrentPlayer().playerId, levelUpAnimKey);
}
}
/**
* @param {number} level
* @returns {string|boolean}
*/
getLevelUpAnimationKey(level)
{
let animationsListObj = this.gameManager.config.client.levels.animations;
let exactKey = 'level_'+this.gameManager.playerData.avatarKey+'_'+level;
if(sc.hasOwn(animationsListObj, exactKey)){
return exactKey;
}
let avatarKey = 'level_'+this.gameManager.playerData.avatarKey;
if(sc.hasOwn(animationsListObj, avatarKey)){
return avatarKey;
}
let levelKey = 'level_'+level;
if(sc.hasOwn(animationsListObj, levelKey)){
return levelKey;
}
if(sc.hasOwn(animationsListObj, 'level_default')){
return 'level_default';
}
return false;
}
/**
* @param {SkillMessage} message
*/
onLevelExperienceAdded(message)
{
this.gameManager.gameDom.updateContent(
ActionsConst.SELECTORS.CURRENT_EXPERIENCE,
this.translator.t(
ActionsConst.SNIPPETS.EXPERIENCE,
{experience: message.data[ActionsConst.MESSAGE.DATA.EXPERIENCE]}
)
);
}
/**
* @param {SkillMessage} message
*/
onSkillBeforeCast(message)
{
this.playSkillPlayerAnimation(
message.data.extraData[ActionsConst.DATA_OWNER_KEY],
this.determineCastKey(message)
);
}
/**
* @param {SkillMessage} message
* @returns {string}
*/
determineCastKey(message)
{
let castKey = message.data.skillKey + '_cast';
if(sc.hasOwn(this.gameManager.config.client.skills.animations, castKey)){
return castKey;
}
return 'default_cast';
}
/**
* @param {string|number} ownerId
* @param {string} animationKey
* @returns {boolean|void}
*/
playSkillPlayerAnimation(ownerId, animationKey)
{
let currentScene = this.gameManager.getActiveScene();
let sceneAnimation = currentScene.getAnimationByKey(animationKey);
if(!sceneAnimation){
if(-1 === animationKey.indexOf('default')){
Logger.error(
'Animation sprite not found for "'+animationKey+'".',
this.gameManager.config.client.skills.animations
);
}
return false;
}
let ownerSprite = this.gameManager.getCurrentPlayer().players[ownerId];
let spriteX = ownerSprite.x;
let spriteY = ownerSprite.y;
let animationSprite = currentScene.physics.add.sprite(spriteX, spriteY, animationKey);
// the default value will be the caster depth - 1 so the animation will be played below the player.
let depth = sc.hasOwn(sceneAnimation, 'depthByPlayer') && 'above' === sceneAnimation['depthByPlayer']
? ownerSprite.depth + 1 : ownerSprite.depth - 0.1;
animationSprite.depthByPlayer = sceneAnimation.depthByPlayer;
animationSprite.setDepth(depth);
let blockMovement = sc.get(sceneAnimation, 'blockMovement', false);
if(!blockMovement){
ownerSprite.moveSprites[animationKey+'_'+ownerSprite.playerId] = animationSprite;
}
animationSprite.anims.play(animationKey, true);
let destroyTime = sc.get(sceneAnimation, 'destroyTime', false);
if(destroyTime){
setTimeout(() => {
animationSprite.destroy();
delete ownerSprite.moveSprites[animationKey+'_'+ownerSprite.playerId];
}, destroyTime);
}
}
/**
* @param {SkillMessage} message
* @returns {boolean|void}
*/
onSkillAfterCast(message)
{
let currentPlayer = this.gameManager.getCurrentPlayer();
if(
!sc.hasOwn(message.data.extraData, ActionsConst.DATA_OWNER_TYPE)
|| !sc.hasOwn(message.data.extraData, ActionsConst.DATA_OWNER_KEY)
|| message.data.extraData[ActionsConst.DATA_OWNER_TYPE] !== ActionsConst.DATA_TYPE_VALUE_PLAYER
|| !sc.hasOwn(currentPlayer.players, message.data.extraData[ActionsConst.DATA_OWNER_KEY])
){
return false;
}
let currentScene = this.gameManager.getActiveScene();
let ownerSprite = this.gameManager.getCurrentPlayer()
.players[message.data.extraData[ActionsConst.DATA_OWNER_KEY]];
let playDirection = this.getPlayDirection(message.data.extraData, ownerSprite, currentPlayer, currentScene);
if(playDirection){
ownerSprite.anims.play(ownerSprite.avatarKey+'_'+playDirection, true);
ownerSprite.anims.stop();
}
}
/**
* @param {SkillMessage} message
* @returns {boolean|void}
*/
onSkillAttackApplyDamage(message)
{
let damageConfig = this.gameManager.config.get('client/actions/damage');
if(!damageConfig.enabled){
return false;
}
let currentPlayer = this.gameManager.getCurrentPlayer();
if(!damageConfig.showAll && message.data.extraData[ActionsConst.DATA_OWNER_KEY] !== currentPlayer.playerId){
return false;
}
let currentScene = this.gameManager.getActiveScene();
let target = currentScene.getObjectFromExtraData(
ActionsConst.DATA_OBJECT_KEY_TARGET,
message.data.extraData,
currentPlayer
);
if(!target){
return false;
}
currentScene.createFloatingText(
target.x,
target.y,
message.data.d,
damageConfig.color,
damageConfig.font,
damageConfig.fontSize,
damageConfig.duration,
damageConfig.top,
damageConfig.stroke,
damageConfig.strokeThickness,
damageConfig.shadowColor
);
}
/**
* @param {Object} extraData
* @param {Object} ownerSprite
* @param {Object} currentPlayer
* @param {PhaserScene} currentScene
* @returns {string|boolean}
*/
getPlayDirection(extraData, ownerSprite, currentPlayer, currentScene)
{
let playDirection = false;
let target = currentScene.getObjectFromExtraData(ActionsConst.DATA_OBJECT_KEY_TARGET, extraData, currentPlayer);
if(!target){
return false;
}
let playX = target.x - ownerSprite.x;
let playY = target.y - ownerSprite.y;
playDirection = (playX >= 0) ? GameConst.RIGHT : GameConst.LEFT;
if(Math.abs(playX) < Math.abs(playY)){
playDirection = (playY >= 0) ? GameConst.DOWN : GameConst.UP;
}
return playDirection;
}
}
module.exports.ReceiverWrapper = ReceiverWrapper;
================================================
FILE: lib/actions/client/skills-ui.js
================================================
/**
*
* Reldens - SkillsUi
*
* Manages the user interface for skills display and interaction.
*
*/
const { ActionsConst } = require('../constants');
/**
* @typedef {import('phaser').Scene} PhaserScene
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
*/
class SkillsUi
{
/**
* @param {PhaserScene} uiScene
*/
constructor(uiScene)
{
/** @type {PhaserScene} */
this.uiScene = uiScene;
/** @type {GameManager} */
this.gameManager = this.uiScene.gameManager;
/** @type {string|null} */
this.defaultAction = this.gameManager.config.get('client/ui/controls/defaultActionKey');
}
createUi()
{
let selector = ActionsConst.SELECTORS.UI_PLAYER_EXTRAS;
this.appendToUiContainer(selector, 'skillsClassPath');
this.appendToUiContainer(selector, 'skillsLevel');
this.appendToUiContainer(selector, 'skillsExperience', {
experienceLabel: this.gameManager.services.translator.t(ActionsConst.SNIPPETS.EXPERIENCE_LABEL)
});
this.createUiBox('skills', 7);
}
/**
* @param {Object} skills
* @returns {boolean|void}
*/
appendSkills(skills)
{
// @TODO - BETA - Implement skills groups.
let skillsList = Object.keys(skills);
// if the default action is a skill we won't show a duplicated box:
if(0 === skillsList.length){
return false;
}
for(let i of skillsList){
let skill = skills[i];
if(skill === this.defaultAction){
continue;
}
this.createSkillBox(skill);
}
}
/**
* @param {string} containerSelector
* @param {string} skillsUiTemplate
* @param {Object} [snippets]
*/
appendToUiContainer(containerSelector, skillsUiTemplate, snippets = {})
{
let messageTemplate = this.uiScene.cache.html.get(skillsUiTemplate);
let snippetsKeys = Object.keys(snippets);
if(0 < snippetsKeys.length){
messageTemplate = this.gameManager.gameEngine.parseTemplate(messageTemplate, snippets);
}
this.gameManager.gameDom.appendToElement(containerSelector, messageTemplate);
}
/**
* @param {string} codeName
* @param {number} depth
*/
createUiBox(codeName, depth)
{
// @TODO - BETA - Replace by UserInterface.
let {uiX, uiY} = this.uiScene.getUiConfig(codeName);
let generatedUi = this.uiScene.add.dom(uiX, uiY).createFromCache(codeName);
generatedUi.setDepth(depth);
this.uiScene.elementsUi[codeName] = generatedUi;
}
/**
* @param {string} skill
*/
createSkillBox(skill)
{
let skillBox = this.parseSkillTemplate(skill);
this.gameManager.gameDom.appendToElement(ActionsConst.SELECTORS.SKILLS_CONTAINER, skillBox);
this.uiScene.setupActionButtonInBox(skill, this.uiScene.getUiElement('skills'));
}
/**
* @param {string} skill
* @returns {string}
*/
parseSkillTemplate(skill)
{
let skillTemplate = this.uiScene.cache.html.get('skillBox');
return this.gameManager.gameEngine.parseTemplate(skillTemplate, {
key: skill,
// @TODO - BETA - Get all the required skill data on the client, from the label to the delay time counter.
skillName: skill
});
}
}
module.exports.SkillsUi = SkillsUi;
================================================
FILE: lib/actions/client/snippets/en_US.js
================================================
/**
*
* Reldens - Translations - en_US
*
*/
module.exports = {
actions: {
selectClassPath: 'Select Your Class-Path',
currentLevel: 'Level %currentLevel',
experience: '%experience',
experienceLabel: 'XP',
classPathLabel: '%classPathLabel',
nextLevelExperience: '%nextLevelExperience'
}
}
================================================
FILE: lib/actions/constants.js
================================================
/**
*
* Reldens - ActionsConst
*
*/
let snippetsPrefix = 'actions.';
module.exports.ActionsConst = {
BATTLE_TYPE_PER_TARGET: 'bt',
BATTLE_TYPE_GENERAL: 'bg',
BATTLE_ENDED: 'bend',
TARGET_POSITION: 'tgp',
TARGET_PLAYER: 'tga',
TARGET_OBJECT: 'tgo',
FULL_SKILLS_LIST: 'fkl',
ACTION: 'action',
DATA_OBJECT_KEY_TARGET: 't',
DATA_OBJECT_KEY_OWNER: 'o',
DATA_TARGET_TYPE: 'tT',
DATA_TARGET_KEY: 'tK',
DATA_OWNER_TYPE: 'oT',
DATA_OWNER_KEY: 'oK',
DATA_TYPE_VALUE_ENEMY: 'e',
DATA_TYPE_VALUE_PLAYER: 'p',
DATA_TYPE_VALUE_OBJECT: 'o',
EXTRA_DATA: {
KEY: 'sked',
SKILL_DELAY: 'sd'
},
DEFAULT_HIT_ANIMATION_KEY: 'default_hit',
ACTIONS: {
SUFFIX: {
ATTACK: '_atk',
EFFECT: '_eff',
HIT: '_hit'
}
},
MESSAGE: {
DATA: {
LEVEL: 'lvl',
EXPERIENCE: 'exp',
CLASS_PATH_LABEL: 'lab',
NEXT_LEVEL_EXPERIENCE: 'ne',
SKILL_LEVEL: 'skl',
LAST_ATTACK_KEY: 'k'
},
DATA_VALUES: {
NAMESPACE: 'actions',
lvl: 'level',
exp: 'experience',
lab: 'classPathLabel',
ne: 'nextLevelExperience',
skl: 'skillLevel'
}
},
SELECTORS: {
LEVEL_LABEL: '.level-container .level-label',
CURRENT_EXPERIENCE: '.experience-container .current-experience',
NEXT_LEVEL_EXPERIENCE: '.experience-container .next-level-experience',
PLAYER_CREATE_FORM: '#player-create-form',
UI_PLAYER_EXTRAS: '#ui-player-extras',
PLAYER_CREATION_ADDITIONAL_INFO: '.player-creation-additional-info',
PLAYER_SELECTION_ADDITIONAL_INFO: '.player-selection-additional-info',
CLASS_PATH_LABEL: '.class-path-container .class-path-label',
SKILLS_CONTAINER: '.skills-container'
},
SNIPPETS: {
PREFIX: snippetsPrefix,
SELECT_CLASS_PATH: snippetsPrefix+'selectClassPath',
EXPERIENCE_LABEL: snippetsPrefix+'experienceLabel',
LEVEL: snippetsPrefix+'currentLevel',
CLASS_PATH_LABEL: snippetsPrefix+'classPathLabel',
NEXT_LEVEL_EXPERIENCE: snippetsPrefix+'nextLevelExperience',
EXPERIENCE: snippetsPrefix+'experience'
}
};
================================================
FILE: lib/actions/factories/class-path-key-factory.js
================================================
/**
*
* Reldens - ClassPathKeyFactory
*
* Generates standardized keys from class path labels.
*
*/
class ClassPathKeyFactory
{
/**
* @param {string} label
* @returns {string}
*/
static fromLabel(label)
{
return label.toLowerCase().replace(/ /g, '-').replace('---', '-');
}
}
module.exports.ClassPathKeyFactory = ClassPathKeyFactory;
================================================
FILE: lib/actions/factories/skill-data-factory.js
================================================
/**
*
* Reldens - SkillDataFactory
*
* Factory for creating and validating skill data structures.
*
*/
const { SkillSchema } = require('../schemas/skill-schema');
const { SchemaValidator, sc, Logger} = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').SchemaValidator} SchemaValidator
*/
class SkillDataFactory
{
constructor()
{
/** @type {number|null} */
this.id = null;
/** @type {string|null} */
this.key = null;
/** @type {string|null} */
this.type = null;
/** @type {boolean|null} */
this.autoValidation = null;
/** @type {number|null} */
this.skillDelay = null;
/** @type {number|null} */
this.castTime = null;
/** @type {number|null} */
this.usesLimit = null;
/** @type {number|null} */
this.range = null;
/** @type {boolean|null} */
this.rangeAutomaticValidation = null;
/** @type {string|null} */
this.rangePropertyX = null;
/** @type {string|null} */
this.rangePropertyY = null;
/** @type {string|null} */
this.rangeTargetPropertyX = null;
/** @type {string|null} */
this.rangeTargetPropertyY = null;
/** @type {boolean|null} */
this.allowSelfTarget = null;
/** @type {number|null} */
this.criticalChance = null;
/** @type {number|null} */
this.criticalMultiplier = null;
/** @type {number|null} */
this.criticalFixedValue = null;
/** @type {Object|null} */
this.customData = null;
/** @type {Array} */
this.classPaths = [];
/** @type {Array} */
this.objects = [];
/** @type {Array} */
this.animations = [];
/** @type {Object|null} */
this.attack = null;
/** @type {Object|null} */
this.physicalData = null;
/** @type {Array} */
this.targetEffects = [];
/** @type {Array} */
this.ownerEffects = [];
/** @type {Array} */
this.ownerConditions = [];
/** @type {Array} */
this.clearPrevious = [];
/** @type {Object} */
this.schema = SkillSchema;
/** @type {SchemaValidator} */
this.schemaValidator = new SchemaValidator(this.schema);
}
/**
* @returns {boolean}
*/
isValid()
{
return this.schemaValidator.validate(this);
}
/**
* @param {string} key
* @param {Object} data
* @param {Object} defaults
* @returns {SkillDataFactory}
*/
mapData(key, data, defaults)
{
Object.assign(this, {key}, sc.get(defaults, 'properties', {}), sc.get(data, 'properties', {}));
this.clearPrevious = sc.get(data, 'clearPrevious', []);
let skillType = sc.get(data.typeData, 'key', false);
if(skillType){
let typeDefaults = sc.get(defaults, skillType, {});
this[skillType] = Object.assign({}, typeDefaults, sc.get(data.typeData, 'properties', {}));
}
let physicalData = sc.get(data, 'physicalData', false);
if(physicalData){
this.physicalData = Object.assign({}, sc.get(defaults, 'physicalData', {}), physicalData);
}
this.mapTargetEffects(data, defaults);
this.mapOwnerEffects(data, defaults);
this.mapOwnerConditions(data, defaults);
this.mapClassPaths(data, defaults);
this.mapObjects(data, defaults);
this.mapAnimations(key, data, defaults);
return this;
}
/**
* @param {Object} data
* @param {Object} defaults
*/
mapClassPaths(data, defaults)
{
let classPathsKeys = Object.keys(defaults.classPaths);
if(0 === classPathsKeys.length){
return;
}
let relatedClassPaths = sc.get(data, 'classPathLevelRelations', {});
let relatedClassPathsKeys = Object.keys(relatedClassPaths);
let relateAll = -1 !== relatedClassPathsKeys.indexOf('all');
let loopClassPaths = relateAll ? Object.keys(defaults.classPaths) : relatedClassPathsKeys;
for(let i of loopClassPaths){
let existentClassPath = defaults.classPaths[i];
if(!existentClassPath){
Logger.warning('Class path not found: "'+i+'".');
continue;
}
let loopIndex = relateAll ? 'all' : i;
let relatedClassPathLevel = existentClassPath.relatedLevels[relatedClassPaths[loopIndex]];
if(!relatedClassPathLevel){
Logger.warning('Level "'+relatedClassPaths[i]+'" not found in class path: "'+loopIndex+'".');
continue;
}
this.classPaths.push({class_path_id: existentClassPath.id, level_id: relatedClassPathLevel.id});
}
}
/**
* @param {Object} data
* @param {Object} defaults
*/
mapObjects(data, defaults)
{
let objectsRelations = sc.get(data, 'objectsRelations', {});
let objectsRelationsKeys = Object.keys(objectsRelations);
if(0 === objectsRelationsKeys.length){
return;
}
for(let i of objectsRelationsKeys){
this.objects.push({objectKey: i, target_id: defaults.targetOptions[objectsRelations[i]].id});
}
}
/**
* @param {string} key
* @param {Object} data
* @param {Object} defaults
*/
mapAnimations(key, data, defaults)
{
let animations = sc.get(data, 'animations', {});
let animationKeys = Object.keys(animations);
if(0 === animationKeys.length){
return;
}
let animationsDefaults = sc.get(defaults, 'animations', {});
let animationDefaultData = animationsDefaults.defaults;
for(let i of animationKeys){
let animationData = Object.assign({}, animationDefaultData, animationsDefaults[i], animations[i]);
if(animationsDefaults.appendSkillKeyOnAnimationImage || animations[i].appendSkillKeyOnAnimationImage){
animationData.img = key + animationData.img;
}
this.animations.push({key: i, animationData});
}
}
/**
* @param {Object} data
* @param {Object} defaults
*/
mapOwnerConditions(data, defaults)
{
let ownerConditions = sc.get(data, 'ownerConditions', []);
if(0 === ownerConditions.length){
return;
}
for(let ownerCondition of ownerConditions){
this.ownerConditions.push(
Object.assign(
{},
sc.get(defaults, 'ownerConditions', {}),
ownerCondition,
{property_key: ownerCondition.propertyKey}
)
);
}
}
/**
* @param {Object} data
* @param {Object} defaults
*/
mapOwnerEffects(data, defaults)
{
let ownerEffects = sc.get(data, 'ownerEffects', []);
if(0 === ownerEffects.length){
return;
}
for(let ownerEffect of ownerEffects){
this.ownerEffects.push(
Object.assign(
{},
sc.get(defaults, 'ownerEffects', {}),
ownerEffect,
{property_key: ownerEffect.propertyKey}
)
);
}
}
/**
* @param {Object} data
* @param {Object} defaults
*/
mapTargetEffects(data, defaults)
{
let targetEffects = sc.get(data, 'targetEffects', []);
if(0 === targetEffects.length){
return;
}
for(let targetEffect of targetEffects){
this.targetEffects.push(
Object.assign(
{},
sc.get(defaults, 'targetEffects', {}),
targetEffect,
{property_key: targetEffect.propertyKey}
)
);
}
}
/**
* @returns {Object}
*/
skillBaseData()
{
return {
key: this.key,
type: this.type,
autoValidation: this.autoValidation,
skillDelay: this.skillDelay,
castTime: this.castTime,
usesLimit: this.usesLimit,
range: this.range,
rangeAutomaticValidation: this.rangeAutomaticValidation,
rangePropertyX: this.rangePropertyX,
rangePropertyY: this.rangePropertyY,
rangeTargetPropertyX: this.rangeTargetPropertyX,
rangeTargetPropertyY: this.rangeTargetPropertyY,
allowSelfTarget: this.allowSelfTarget,
criticalChance: this.criticalChance,
criticalMultiplier: this.criticalMultiplier,
criticalFixedValue: this.criticalFixedValue,
customData: this.customData
};
}
}
module.exports.SkillDataFactory = SkillDataFactory;
================================================
FILE: lib/actions/schemas/skill-schema.js
================================================
/**
*
* Reldens - SkillSchema
*
*/
module.exports.SkillSchema = {
// id: {type: 'int'},
key: {type: 'string'},
type: {type: 'int'},
autoValidation: {type: 'int'},
skillDelay: {type: 'int'},
castTime: {type: 'int'},
usesLimit: {type: 'int'},
range: {type: 'int'},
rangeAutomaticValidation: {type: 'int'},
rangePropertyX: {type: 'string'},
rangePropertyY: {type: 'string'},
// rangeTargetPropertyX: {type: 'string'},
// rangeTargetPropertyY: {type: 'string'},
allowSelfTarget: {type: 'int'},
// criticalChance: {type: 'int'},
// criticalMultiplier: {type: 'int'},
// criticalFixedValue: {type: 'int'},
// customData: {type: 'string'},
};
================================================
FILE: lib/actions/server/battle-end-action.js
================================================
/**
*
* Reldens - BattleEndAction
*
* Represents the end of a battle action with position and target data.
*
*/
const { GameConst } = require('../../game/constants');
const { ActionsConst } = require('../constants');
class BattleEndAction
{
/**
* @param {number} positionX
* @param {number} positionY
* @param {string} targetKey
* @param {string} lastAttackKey
*/
constructor(positionX, positionY, targetKey, lastAttackKey)
{
this[GameConst.ACTION_KEY] = ActionsConst.BATTLE_ENDED;
this.x = positionX;
this.y = positionY;
this[ActionsConst.DATA_OBJECT_KEY_TARGET] = targetKey
this[ActionsConst.MESSAGE.DATA.LAST_ATTACK_KEY] = lastAttackKey
}
}
module.exports.BattleEndAction = BattleEndAction;
================================================
FILE: lib/actions/server/battle.js
================================================
/**
*
* Reldens - Battle
*
* Base battle system handling combat execution and player death/revive logic.
*
*/
const { BattleEndAction } = require('./battle-end-action');
const { PlayerDeathEvent } = require('./events/player-death-event');
const { ActionsConst } = require('../constants');
const { GameConst } = require('../../game/constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('../../users/server/player').Player} Player
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
*
* @typedef {Object} BattleProps
* @property {number|boolean} [battleTimeOff]
* @property {string} [timerType]
* @property {EventsManager} [events]
*/
class Battle
{
/**
* @param {BattleProps} props
*/
constructor(props)
{
/** @type {Object} */
this.inBattleWith = {};
/** @type {number|boolean} */
this.battleTimeOff = props.battleTimeOff || false;
/** @type {Object|false} */
this.battleTimer = false;
/** @type {Object|false} */
this.playerReviveTimer = false;
/** @type {string} */
this.timerType = props.timerType || ActionsConst.BATTLE_TYPE_PER_TARGET;
/** @type {number|boolean} */
this.lastAttack = false;
/** @type {string|boolean} */
this.lastAttackKey = false;
/** @type {EventsManager|false} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in Battle "'+this.constructor.name+'".');
}
}
/**
* @param {Player} playerSchema
* @param {Object} target
* @returns {Promise}
*/
async runBattle(playerSchema, target)
{
let playerState = playerSchema?.state?.inState;
if(GameConst.STATUS.ACTIVE !== playerState){
Logger.error('Battle inactive player with ID "'+playerSchema.player_id+'".', playerState);
delete this.inBattleWith[target.id];
return false;
}
let targetState = target?.state?.inState;
if(targetState && GameConst.STATUS.ACTIVE.toString() !== targetState.toString()){
//Logger.debug('Inactive target ID "'+(target.uid || target.player_id)+'" in state "'+targetState+'".');
delete this.inBattleWith[target.id];
return false;
}
// @NOTE: each attack will have different properties to validate like range, delay, etc.
let currentAction = this.getCurrentAction(playerSchema);
if(!currentAction){
Logger.error('Actions not defined for this player with ID "'+playerSchema.player_id+'".');
delete this.inBattleWith[target.id];
return false;
}
currentAction.currentBattle = this;
this.lastAttackKey = currentAction.key;
let executeResult = await currentAction.execute(target);
// include the target in the battle list:
this.lastAttack = Date.now();
this.inBattleWith[target.id] = {target: target, time: this.lastAttack, battleTimer: false};
let useTimerObj = this;
if(this.timerType === ActionsConst.BATTLE_TYPE_PER_TARGET){
useTimerObj = this.inBattleWith[target.id];
}
this.setTimerOn(useTimerObj, target);
playerSchema.currentAction = false; // reset action.
return executeResult;
}
/**
* @param {Player} playerSchema
* @returns {Object}
*/
getCurrentAction(playerSchema)
{
let currentAction = playerSchema.currentAction;
return sc.get(
playerSchema.actions,
currentAction,
playerSchema.skillsServer.classPath.currentSkills[currentAction]
);
}
/**
* @param {Object} useTimerObj
* @param {Object} target
*/
setTimerOn(useTimerObj, target)
{
if(useTimerObj.battleTimer){
clearTimeout(useTimerObj.battleTimer);
delete this.inBattleWith[target.id];
}
if(this.battleTimeOff){
useTimerObj.battleTimer = setTimeout(() => {
delete this.inBattleWith[target.id];
}, this.battleTimeOff);
}
}
/**
* @param {Object} targetClient
* @param {Player} targetSchema
* @param {string|number} attackerId
* @param {RoomScene} room
* @param {Player} [attackerPlayer]
* @returns {Promise}
*/
async updateTargetClient(targetClient, targetSchema, attackerId, room, attackerPlayer)
{
if(!targetSchema.stats){
Logger.warning('Target schema for player (ID: '+targetSchema?.player_id+') with undefined stats.');
return false;
}
let affectedProperty = room.config.get('client/actions/skills/affectedProperty');
if(0 === targetSchema.stats[affectedProperty]){
return await this.clientDeathUpdate(
targetSchema,
room,
targetClient,
affectedProperty,
attackerId,
attackerPlayer
);
}
return await room.savePlayerStats(targetSchema, targetClient);
}
/**
* @param {Player} targetSchema
* @param {RoomScene} room
* @param {Object} targetClient
* @param {string} affectedProperty
* @param {string|number} attackerId
* @param {Player} [attackerPlayer]
* @returns {Promise}
*/
async clientDeathUpdate(targetSchema, room, targetClient, affectedProperty, attackerId, attackerPlayer)
{
if(!targetSchema.player_id){
Logger.error('Target is not a player.', targetSchema.player_id);
return false;
}
if(targetSchema.isDeath() || targetSchema.isDisabled()){
//Logger.debug('Target is already death.', targetSchema.player_id);
// expected when multiple enemies get the player life = 0 as response from the last hit in battle:
return false;
}
//Logger.debug('Player with ID "'+targetSchema.player_id+'" is death.');
room.deactivatePlayer(targetSchema, GameConst.STATUS.DEATH);
let actionData = new BattleEndAction(
targetSchema.state.x,
targetSchema.state.y,
targetSchema.sessionId,
this.lastAttackKey
);
let body = targetSchema.physicalBody;
room.roomWorld.removeBodies.push(body);
room.broadcast('*', actionData);
await room.savePlayerState(targetSchema.sessionId);
targetClient.send('*', {act: GameConst.GAME_OVER});
await room.savePlayerStats(targetSchema, targetClient);
targetSchema.setPrivate(
'playerDeathTimer',
this.playerReviveTimer = setTimeout(
async () => {
return await this.revivePlayer(room, body, targetSchema, affectedProperty, targetClient);
},
(room.config.get('server/players/gameOver/timeOut') || 1)
)
);
room.events.emit('reldens.playerDeath', new PlayerDeathEvent({
targetSchema,
room,
targetClient,
affectedProperty,
attackerPlayer
}));
return false;
}
/**
* @param {RoomScene} room
* @param {Object} body
* @param {Player} targetSchema
* @param {string} affectedProperty
* @param {Object} targetClient
* @returns {Promise}
*/
async revivePlayer(room, body, targetSchema, affectedProperty, targetClient)
{
if(!room.roomWorld){
Logger.critical('Room world not available to set player death timer.');
return false;
}
let sessionId = targetSchema.sessionId;
if(!room.activePlayerBySessionId(sessionId, room.roomId)){
// @NOTE: expected if the player disconnected while is death and timer keeps running after disconnection.
return false;
}
try {
if(sc.isFunction(room.roomWorld.addBody)){
room.roomWorld.addBody(body);
}
room.activatePlayer(targetSchema, GameConst.STATUS.ACTIVE);
// player is dead! reinitialize the stats using its base value:
targetSchema.stats[affectedProperty] = targetSchema.statsBase[affectedProperty];
await room.savePlayerStats(targetSchema, targetClient);
room.broadcast('*', {act: GameConst.REVIVED, t: sessionId});
//Logger.debug('Revived: '+targetSchema.player_id+' ('+ sessionId+' / '+targetSchema.state.inState+')');
return true;
} catch (error) {
Logger.error('There was an error on setting player death timer. ' + error.message);
}
return false;
}
}
module.exports.Battle = Battle;
================================================
FILE: lib/actions/server/data-loader.js
================================================
/**
*
* Reldens - DataLoader
*
* Loads skills, class paths, and animation data from the database into configuration.
*
*/
const { TypeAttack, TypeEffect, TypePhysicalAttack, TypePhysicalEffect } = require('./skills/types');
const { SkillConst} = require('@reldens/skills');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
* @typedef {import('./models-manager').ModelsManager} ModelsManager
*/
class DataLoader
{
/**
* @param {Object} configProcessor
* @param {ModelsManager} skillsModelsManager
* @param {BaseDataServer} dataServer
* @returns {Promise}
*/
static async enrichConfig(configProcessor, skillsModelsManager, dataServer)
{
await this.prepareConfigProcessor(configProcessor);
await this.loadSkillsFullList(configProcessor, skillsModelsManager);
await this.loadGroupsFullList(configProcessor, dataServer);
await this.loadClassPathFullList(configProcessor, skillsModelsManager);
await this.appendSkillsAnimations(configProcessor, dataServer);
await this.appendLevelsAnimations(configProcessor, dataServer);
}
/**
* @param {Object} configProcessor
* @returns {Promise}
*/
static async prepareConfigProcessor(configProcessor)
{
if(!sc.hasOwn(configProcessor, 'skills')){
configProcessor.skills = {skillsList: {}};
}
if(!sc.hasOwn(configProcessor.skills, 'defaultSkills')){
configProcessor.skills.defaultSkills = {};
}
configProcessor.skills.defaultSkills[SkillConst.SKILL.TYPE.ATTACK] = TypeAttack;
configProcessor.skills.defaultSkills[SkillConst.SKILL.TYPE.EFFECT] = TypeEffect;
configProcessor.skills.defaultSkills[SkillConst.SKILL.TYPE.PHYSICAL_ATTACK] = TypePhysicalAttack;
configProcessor.skills.defaultSkills[SkillConst.SKILL.TYPE.PHYSICAL_EFFECT] = TypePhysicalEffect;
return configProcessor;
}
/**
* @param {Object} configProcessor
* @param {ModelsManager} skillsModelsManager
* @returns {Promise}
*/
static async loadSkillsFullList(configProcessor, skillsModelsManager)
{
let skillsClasses = configProcessor.getWithoutLogs('server/customClasses/skills/skillsList', {});
// defined in this same class on the reldens.serverReady listener:
Object.assign(skillsClasses, configProcessor.skills.defaultSkills);
configProcessor.skills = await skillsModelsManager.generateSkillsDataFromModels(skillsClasses);
}
/**
* @param {Object} configProcessor
* @param {BaseDataServer} dataServer
* @returns {Promise}
*/
static async loadGroupsFullList(configProcessor, dataServer)
{
let groupsModels = await dataServer.getEntity('skillsGroups').loadAll();
if(0 < groupsModels.length){
configProcessor.skills.groups = groupsModels;
}
}
/**
* @param {Object} configProcessor
* @param {ModelsManager} skillsModelsManager
* @returns {Promise}
*/
static async loadClassPathFullList(configProcessor, skillsModelsManager)
{
configProcessor.skills.classPaths = await skillsModelsManager.generateClassPathInstances(
configProcessor.getWithoutLogs('server/customClasses/skills/classPath', {})
);
//Logger.debug('Config Processor skills class paths:', configProcessor.skills.classPaths);
}
/**
* @param {Object} config
* @param {BaseDataServer} dataServer
* @returns {Promise}
*/
static async appendSkillsAnimations(config, dataServer)
{
let animationsModels = await dataServer.getEntity('skillsSkillAnimations').loadAllWithRelations();
if(0 === animationsModels.length){
Logger.debug('None animations models found.');
return config.client.skills.animations;
}
for(let skillAnim of animationsModels){
let animationData = sc.toJson(skillAnim.animationData, {});
let customDataJson = sc.toJson(skillAnim.related_skills_skill.customData, {});
if(sc.hasOwn(customDataJson, 'blockMovement')){
animationData.blockMovement = customDataJson.blockMovement;
}
config.client.skills.animations[skillAnim.related_skills_skill.key+'_'+skillAnim.key] = {
skillId: skillAnim.skill_id,
skillKey: skillAnim.related_skills_skill.key,
key: skillAnim.key,
class: skillAnim.classKey,
animationData
}
}
return config.client.skills.animations;
}
/**
* @param {Object} config
* @param {BaseDataServer} dataServer
* @returns {Promise}
*/
static async appendLevelsAnimations(config, dataServer)
{
if(!sc.hasOwn(config.client, 'levels')){
config.client.levels = {};
}
if(!sc.hasOwn(config.client.levels, 'animations')){
config.client.levels.animations = {};
}
let levelsAnimationsModels = await dataServer.getEntity('skillsClassLevelUpAnimations').loadAllWithRelations();
if(0 === levelsAnimationsModels.length){
return config.client.levels.animations;
}
for(let levelAnimation of levelsAnimationsModels){
let animationData = sc.toJson(levelAnimation.animationData, {});
let animationKey = this.generateAnimationKey(levelAnimation);
config.client.levels.animations[animationKey] = {
key: animationKey,
levelId: sc.get(levelAnimation.related_skills_levels, 'id', null),
classKey: sc.get(levelAnimation.related_skills_class_path, 'key', null),
animationData
}
}
return config.client.levels.animations;
}
/**
* @param {Object} levelAnimation
* @returns {string}
*/
static generateAnimationKey(levelAnimation)
{
let levelKey = sc.get(levelAnimation.related_skills_levels, 'id', '');
let classPathKey = sc.get(levelAnimation.related_skills_class_path, 'key', '');
if('' === levelKey && '' === classPathKey){
return 'level_default';
}
let animationKey = 'level';
if('' !== classPathKey){
classPathKey = '_'+classPathKey;
}
if('' !== levelKey){
levelKey = '_'+levelKey;
}
return animationKey+classPathKey+levelKey;
}
}
module.exports.DataLoader = DataLoader;
================================================
FILE: lib/actions/server/entities/operation-types-entity-override.js
================================================
/**
*
* Reldens - OperationTypesEntityOverride
*
* Custom entity override for operation types with admin panel configuration.
*
*/
const { OperationTypesEntity } = require('../../../../generated-entities/entities/operation-types-entity');
class OperationTypesEntityOverride extends OperationTypesEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.navigationPosition = 2020;
return config;
}
}
module.exports.OperationTypesEntityOverride = OperationTypesEntityOverride;
================================================
FILE: lib/actions/server/entities/skills-class-path-entity-override.js
================================================
/**
*
* Reldens - SkillsClassPathEntityOverride
*
* Entity override for skills class path with admin navigation configuration.
*
*/
const { SkillsClassPathEntity } = require('../../../../generated-entities/entities/skills-class-path-entity');
class SkillsClassPathEntityOverride extends SkillsClassPathEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.navigationPosition = 110;
return config;
}
}
module.exports.SkillsClassPathEntityOverride = SkillsClassPathEntityOverride;
================================================
FILE: lib/actions/server/entities/skills-levels-modifiers-entity-override.js
================================================
/**
*
* Reldens - SkillsLevelsModifiersEntityOverride
*
* Entity override for skills level modifiers with property configuration.
*
*/
const { SkillsLevelsModifiersEntity } = require(
'../../../../generated-entities/entities/skills-levels-modifiers-entity'
);
const { sc } = require('@reldens/utils');
class SkillsLevelsModifiersEntityOverride extends SkillsLevelsModifiersEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.listProperties = sc.removeFromArray(config.listProperties, [
'minValue',
'maxValue',
'minProperty',
'maxProperty'
]);
return config;
}
}
module.exports.SkillsLevelsModifiersEntityOverride = SkillsLevelsModifiersEntityOverride;
================================================
FILE: lib/actions/server/entities/skills-levels-set-entity-override.js
================================================
/**
*
* Reldens - SkillsLevelsSetEntityOverride
*
* Entity override for skills levels set with admin navigation configuration.
*
*/
const { SkillsLevelsSetEntity } = require('../../../../generated-entities/entities/skills-levels-set-entity');
class SkillsLevelsSetEntityOverride extends SkillsLevelsSetEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.navigationPosition = 100;
return config;
}
}
module.exports.SkillsLevelsSetEntityOverride = SkillsLevelsSetEntityOverride;
================================================
FILE: lib/actions/server/entities/skills-owners-class-path-entity-override.js
================================================
/**
*
* Reldens - SkillsOwnersClassPathEntityOverride
*
* Entity override for skills owners class path with property configuration.
*
*/
const { SkillsOwnersClassPathEntity } = require(
'../../../../generated-entities/entities/skills-owners-class-path-entity'
);
class SkillsOwnersClassPathEntityOverride extends SkillsOwnersClassPathEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.navigationPosition = 990;
return config;
}
}
module.exports.SkillsOwnersClassPathEntityOverride = SkillsOwnersClassPathEntityOverride;
================================================
FILE: lib/actions/server/entities/skills-skill-animations-entity-override.js
================================================
/**
*
* Reldens - SkillsSkillAnimationsEntityOverride
*
* Entity override for skills animations with property configuration.
*
*/
const { SkillsSkillAnimationsEntity } = require(
'../../../../generated-entities/entities/skills-skill-animations-entity'
);
class SkillsSkillAnimationsEntityOverride extends SkillsSkillAnimationsEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.listProperties.splice(config.listProperties.indexOf('classKey'), 1);
return config;
}
}
module.exports.SkillsSkillAnimationsEntityOverride = SkillsSkillAnimationsEntityOverride;
================================================
FILE: lib/actions/server/entities/skills-skill-attack-entity-override.js
================================================
/**
*
* Reldens - SkillsSkillAttackEntityOverride
*
* Entity override for skills attack with property configuration.
*
*/
const { SkillsSkillAttackEntity } = require('../../../../generated-entities/entities/skills-skill-attack-entity');
const { sc } = require('@reldens/utils');
class SkillsSkillAttackEntityOverride extends SkillsSkillAttackEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.listProperties = sc.removeFromArray(config.listProperties, [
'allowEffectBelowZero',
'applyDirectDamage',
'dodgeFullEnabled',
'dodgeOverAimSuccess',
'damageAffected',
'criticalAffected'
]);
return config;
}
}
module.exports.SkillsSkillAttackEntityOverride = SkillsSkillAttackEntityOverride;
================================================
FILE: lib/actions/server/entities/skills-skill-entity-override.js
================================================
/**
*
* Reldens - SkillsSkillEntityOverride
*
* Entity override for skills with property and navigation configuration.
*
*/
const { SkillsSkillEntity } = require('../../../../generated-entities/entities/skills-skill-entity');
const { sc } = require('@reldens/utils');
class SkillsSkillEntityOverride extends SkillsSkillEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.listProperties = sc.removeFromArray(config.listProperties, [
'autoValidation',
'rangeAutomaticValidation',
'rangePropertyX',
'rangePropertyY',
'rangeTargetPropertyX',
'rangeTargetPropertyY',
'allowSelfTarget',
'criticalChance',
'criticalMultiplier',
'criticalFixedValue'
]);
config.navigationPosition = 200;
return config;
}
}
module.exports.SkillsSkillEntityOverride = SkillsSkillEntityOverride;
================================================
FILE: lib/actions/server/entities/skills-skill-owner-effects-entity-override.js
================================================
/**
*
* Reldens - SkillsSkillOwnerEffectsEntityOverride
*
* Entity override for skills owner effects with property configuration.
*
*/
const { SkillsSkillOwnerEffectsEntity } = require(
'../../../../generated-entities/entities/skills-skill-owner-effects-entity'
);
const { sc } = require('@reldens/utils');
class SkillsSkillOwnerEffectsEntityOverride extends SkillsSkillOwnerEffectsEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.listProperties = sc.removeFromArray(config.listProperties, [
'minValue',
'maxValue',
'minProperty',
'maxProperty'
]);
return config;
}
}
module.exports.SkillsSkillOwnerEffectsEntityOverride = SkillsSkillOwnerEffectsEntityOverride;
================================================
FILE: lib/actions/server/entities/skills-skill-target-effects-entity-override.js
================================================
/**
*
* Reldens - SkillsSkillTargetEffectsEntityOverride
*
* Entity override for skills target effects with property configuration.
*
*/
const { SkillsSkillTargetEffectsEntity } = require(
'../../../../generated-entities/entities/skills-skill-target-effects-entity'
);
const { sc } = require('@reldens/utils');
class SkillsSkillTargetEffectsEntityOverride extends SkillsSkillTargetEffectsEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.listProperties = sc.removeFromArray(config.listProperties, [
'minValue',
'maxValue',
'minProperty',
'maxProperty'
]);
return config;
}
}
module.exports.SkillsSkillTargetEffectsEntityOverride = SkillsSkillTargetEffectsEntityOverride;
================================================
FILE: lib/actions/server/entities-config.js
================================================
/**
*
* Reldens - Entities Config
*
*/
const { SkillsSkillAnimationsEntityOverride } = require('./entities/skills-skill-animations-entity-override');
const { SkillsClassPathEntityOverride } = require('./entities/skills-class-path-entity-override');
const { SkillsLevelsModifiersEntityOverride } = require('./entities/skills-levels-modifiers-entity-override');
const { SkillsLevelsSetEntityOverride } = require('./entities/skills-levels-set-entity-override');
const { OperationTypesEntityOverride } = require('./entities/operation-types-entity-override');
const { SkillsOwnersClassPathEntityOverride } = require('./entities/skills-owners-class-path-entity-override');
const { SkillsSkillAttackEntityOverride } = require('./entities/skills-skill-attack-entity-override');
const { SkillsSkillEntityOverride } = require('./entities/skills-skill-entity-override');
const { SkillsSkillOwnerEffectsEntityOverride } = require('./entities/skills-skill-owner-effects-entity-override');
const { SkillsSkillTargetEffectsEntityOverride } = require('./entities/skills-skill-target-effects-entity-override');
module.exports.entitiesConfig = {
skillsSkillAnimations: SkillsSkillAnimationsEntityOverride,
skillsClassPath: SkillsClassPathEntityOverride,
skillsLevelsModifiers: SkillsLevelsModifiersEntityOverride,
skillsLevelsSet: SkillsLevelsSetEntityOverride,
operationTypes: OperationTypesEntityOverride,
skillsOwnersClassPath: SkillsOwnersClassPathEntityOverride,
skillsSkillAttack: SkillsSkillAttackEntityOverride,
skillsSkill: SkillsSkillEntityOverride,
skillsSkillOwnerEffects: SkillsSkillOwnerEffectsEntityOverride,
skillsSkillTargetEffects: SkillsSkillTargetEffectsEntityOverride
};
================================================
FILE: lib/actions/server/entities-translations.js
================================================
/**
*
* Reldens - Entities Translations
*
*/
module.exports.entitiesTranslations = {
labels: {
skills_class_level_up_animations: 'Level Up Animations',
skills_class_path: 'Class Paths',
skills_class_path_level_labels: 'Levels Labels',
skills_class_path_level_skills: 'Levels Skills',
skills_groups: 'Groups',
skills_levels: 'Levels',
skills_levels_modifiers_conditions: 'Levels Modifiers Conditions',
skills_levels_modifiers: 'Levels Modifiers',
skills_levels_set: 'Levels Sets',
skills_owners_class_path: 'Players Class Path',
skills_skill_animations: 'Animations',
skills_skill_attack: 'Attack Properties',
skills_skill: 'Skills',
skills_skill_group_relation: 'Groups Relation',
skills_skill_owner_conditions: 'Owner Conditions',
skills_skill_owner_effects_conditions: 'Owner Effects Conditions',
skills_skill_owner_effects: 'Owner Effects',
skills_skill_physical_data: 'Physics Data',
skills_skill_target_effects_conditions: 'Target Effects Conditions',
skills_skill_target_effects: 'Target Effects Properties',
skills_skill_type: 'Skill Types',
}
};
================================================
FILE: lib/actions/server/event-listeners.js
================================================
/**
*
* Reldens - EventListeners
*
* Manages event listeners for skill casting and movement blocking.
*
*/
const { SkillsEvents } = require('@reldens/skills');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
*/
class EventListeners
{
/**
* @param {Object} props
* @param {Object} props.classPath
* @param {EventsManager} props.events
* @param {Object} props.actionsPlugin
* @returns {Promise}
*/
static async attachCastMovementEvents(props)
{
let {classPath, events, actionsPlugin} = props;
if(!classPath || !events || !actionsPlugin){
Logger.critical('EventListeners: classPath, events or actionsPlugin undefined.', props);
return;
}
let ownerId = classPath.getOwnerEventKey();
classPath.listenEvent(
SkillsEvents.SKILL_BEFORE_CAST,
async (skill) => {
if(this.validateSkillData(skill)){
return;
}
skill.owner.physicalBody.isBlocked = true;
},
classPath.getOwnerUniqueEventKey('skillBeforeCastPack'),
ownerId
);
classPath.listenEvent(
SkillsEvents.SKILL_AFTER_CAST,
async (skill) => {
if(this.validateSkillData(skill)){
return;
}
skill.owner.physicalBody.isBlocked = false;
},
classPath.getOwnerUniqueEventKey('skillAfterCastPack'),
ownerId
);
await events.emit('reldens.actionsPrepareEventsListeners', actionsPlugin, classPath);
}
/**
* @param {Object} skill
* @returns {boolean}
*/
static validateSkillData(skill)
{
let customDataJson = sc.toJson(skill.customData);
return !customDataJson
|| !sc.get(customDataJson, 'blockMovement', false)
|| !sc.hasOwn(skill.owner, 'physicalBody');
}
}
module.exports.EventListeners = EventListeners;
================================================
FILE: lib/actions/server/events/battle-ended-event.js
================================================
/**
*
* Reldens - BattleEndedEvent
*
* Event emitted when a PvE battle ends.
*
*/
/**
* @typedef {import('../../users/server/player').Player} Player
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
* @typedef {import('../pve').Pve} Pve
*
* @typedef {Object} BattleEndedEventProps
* @property {Player} playerSchema
* @property {Pve} pve
* @property {Object} actionData
* @property {RoomScene} room
*/
class BattleEndedEvent
{
/**
* @param {BattleEndedEventProps} props
*/
constructor(props)
{
/** @type {Player} */
this.playerSchema = props.playerSchema;
/** @type {Pve} */
this.pve = props.pve
/** @type {Object} */
this.actionData = props.actionData;
/** @type {RoomScene} */
this.room = props.room;
}
}
module.exports.BattleEndedEvent = BattleEndedEvent;
================================================
FILE: lib/actions/server/events/player-death-event.js
================================================
/**
*
* Reldens - PlayerDeathEvent
*
* Event emitted when a player dies in combat.
*
*/
/**
* @typedef {import('../../users/server/player').Player} Player
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
*
* @typedef {Object} PlayerDeathEventProps
* @property {Player} targetSchema
* @property {RoomScene} room
* @property {Object} targetClient
* @property {string} affectedProperty
* @property {Player} [attackerPlayer]
*/
class PlayerDeathEvent
{
/**
* @param {PlayerDeathEventProps} props
*/
constructor(props)
{
/** @type {Player} */
this.targetSchema = props.targetSchema;
/** @type {RoomScene} */
this.room = props.room;
/** @type {Object} */
this.targetClient = props.targetClient
/** @type {string} */
this.affectedProperty = props.affectedProperty;
/** @type {Player|undefined} */
this.attackerPlayer = props.attackerPlayer;
}
}
module.exports.PlayerDeathEvent = PlayerDeathEvent;
================================================
FILE: lib/actions/server/initial-game-data-enricher.js
================================================
/**
*
* Reldens - InitialGameDataEnricher
*
* Enriches initial game data with class path labels for the client.
*
*/
/**
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
*/
class InitialGameDataEnricher
{
constructor()
{
/** @type {Object|boolean} */
this.classesData = false;
}
/**
* @param {RoomScene} roomGame
* @param {Object} superInitialGameData
* @returns {Promise}
*/
async withClassPathLabels(roomGame, superInitialGameData)
{
if(!roomGame.config.skills.classPaths.classPathsByKey){
return;
}
if(!this.classesData){
let classPathsLabelsByKey = {};
for(let i of Object.keys(roomGame.config.skills.classPaths.classPathsByKey)){
let classPath = roomGame.config.skills.classPaths.classPathsByKey[i];
classPathsLabelsByKey[classPath.data.id] = {key: i, label: classPath.data.label};
}
this.classesData = classPathsLabelsByKey;
}
superInitialGameData.classesData = this.classesData;
}
}
module.exports.InitialGameDataEnricher = InitialGameDataEnricher;
================================================
FILE: lib/actions/server/message-actions.js
================================================
/**
*
* Reldens - ActionsMessageActions
*
* Processes action messages and validates targets for skill execution.
*
*/
const { GameConst } = require('../../game/constants');
const { ActionsConst } = require('../../actions/constants');
const { ObjectsConst } = require('../../objects/constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@colyseus/core').Client} ColyseusClient
* @typedef {import('../../users/server/player').Player} Player
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
*/
class ActionsMessageActions
{
/**
* @param {ColyseusClient} client
* @param {Object} data
* @param {RoomScene} room
* @param {Player} playerSchema
* @returns {Promise}
*/
async executeMessageActions(client, data, room, playerSchema)
{
let bodyToMove = playerSchema.physicalBody;
if(playerSchema.isCasting || bodyToMove.isBlocked || bodyToMove.isChangingScene){
// if body is blocked do NOTHING! it could be because a scene change, or a skill activation or an item
return false;
}
if(playerSchema.isDeath() || playerSchema.isDisabled()){
return false;
}
if(ActionsConst.ACTION !== data.act || !data.target){
return false;
}
let validTarget = this.validateTarget(data.target, room);
if(!validTarget){
return false;
}
let currentAction = this.preparePlayerCurrentAction(playerSchema, data);
if(!currentAction){
return false;
}
// run skill validations (range, time, conditions, etc):
if(!currentAction.validateRange(validTarget) || !currentAction.validate()){
return false;
}
currentAction.room = room;
if(data.target.type === GameConst.TYPE_PLAYER && playerSchema.actions['pvp']){
await playerSchema.actions['pvp'].runBattle(playerSchema, validTarget, room);
}
if(data.target.type === ObjectsConst.TYPE_OBJECT && sc.hasOwn(validTarget, 'battle')){
await validTarget.battle.runBattle(playerSchema, validTarget, room);
}
}
/**
* @param {Player} playerSchema
* @param {Object} data
* @returns {any|boolean}
*/
preparePlayerCurrentAction(playerSchema, data)
{
let runAction = data.type;
let playerAction = sc.get(playerSchema.actions, runAction, false);
let classPathSkill = sc.get(playerSchema.skillsServer.classPath.currentSkills, runAction, false);
// @NOTE: actions could be anything the player will apply on the target, for example an action button
// could send the type "dig", and that will run an action that will make the player "find something".
// For that matter the action could always validate the target as true anywhere on the ground, so the
// current position / layer on the map could be validated.
// On the other hand skills have their own behavior and in most of the cases it will trigger a battle.
// Skills come from a specific system to which we have direct access from here.
if(!runAction || (!playerAction && !classPathSkill)){
Logger.error('Action not available:', runAction, data);
return false;
}
playerSchema.currentAction = runAction;
// if one is not available because of the condition above the other will be:
return playerAction ? playerAction : classPathSkill;
}
/**
* @param {Object} target
* @param {RoomScene} room
* @returns {any|boolean}
*/
validateTarget(target, room)
{
let validTarget = false;
if(target.type === GameConst.TYPE_PLAYER){
validTarget = room.playerBySessionIdFromState(target.id);
}
if(target.type === ObjectsConst.TYPE_OBJECT){
validTarget = room.objectsManager.roomObjects[target.id];
}
if(target.type === ActionsConst.TARGET_POSITION){
validTarget = target;
}
return validTarget;
}
}
module.exports.ActionsMessageActions = ActionsMessageActions;
================================================
FILE: lib/actions/server/models-manager.js
================================================
/**
*
* Reldens - Skills - ModelsManager
*
* Manages database operations for skills, class paths, and related entities.
*
*/
const { ClassPathGenerator } = require('./storage/class-path-generator');
const { SkillsGenerator } = require('./storage/skills-generator');
const { SkillsClassPathLoader } = require('./skills-class-path-loader');
const { EventsManagerSingleton, Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
* @typedef {import('@reldens/utils').EventsManager} EventsManager
*
* @typedef {Object} ModelsManagerProps
* @property {BaseDataServer} [dataServer]
* @property {EventsManager} [events]
*/
class ModelsManager
{
/**
* @param {ModelsManagerProps} props
*/
constructor(props)
{
/** @type {BaseDataServer|false} */
this.dataServer = sc.get(props, 'dataServer', false);
/** @type {EventsManager} */
this.events = sc.get(props, 'events', EventsManagerSingleton);
}
/**
* @param {string} entityName
* @returns {any|false}
*/
getEntity(entityName)
{
if(!entityName){
Logger.warning('Entity name is missing.');
return false;
}
if(!this.dataServer){
Logger.warning('Data server is missing.');
return false;
}
return this.dataServer.entityManager.get(entityName);
}
/**
* @param {number} ownerId
* @returns {Promise}
*/
async loadOwnerClassPath(ownerId)
{
return await this.getEntity('skillsOwnersClassPath').loadOneByWithRelations(
'owner_id',
ownerId,
'related_skills_class_path'
);
}
/**
* @param {Object} levelsSet
* @returns {Promise}
*/
async updateLevel(levelsSet)
{
return await this.getEntity('skillsOwnersClassPath').updateBy(
'owner_id',
levelsSet.getOwnerId(),
{currentLevel: levelsSet.currentLevel}
);
}
/**
* @param {Object} levelsSet
* @returns {Promise}
*/
async updateExperience(levelsSet)
{
return await this.getEntity('skillsOwnersClassPath').updateBy(
'owner_id',
levelsSet.getOwnerId(),
{currentExp: levelsSet.currentExp}
);
}
/**
* @param {Object} skillsClasses
* @returns {Promise}
*/
async generateSkillsDataFromModels(skillsClasses)
{
// @TODO - BETA - Replace relations by constants on the registered-entities definition.
// This way we will be able to use the get method, save the entity in a variable and call the relations list
// from it.
let skillsModels = await this.getEntity('skillsSkill').loadAllWithRelations([
'related_skills_skill_attack',
'related_skills_skill_physical_data',
'related_skills_skill_owner_conditions',
'related_skills_skill_owner_effects',
'related_skills_skill_target_effects'
]);
//Logger.debug('Skills Models:', skillsModels, 'Skills Classes:', skillsClasses);
return SkillsGenerator.dataFromSkillsModelsWithClasses(skillsModels, skillsClasses, this.events);
}
/**
* @param {Object} classPathClasses
* @returns {Promise}
*/
async generateClassPathInstances(classPathClasses)
{
let loader = new SkillsClassPathLoader({dataServer: this.dataServer});
return ClassPathGenerator.fromClassPathModels(await loader.loadFullPathData(), classPathClasses);
}
/**
* @param {Object} owner
* @param {string} ownerIdProperty
* @param {Object} classPathsListById
* @param {Object} skillsClassesList
* @returns {Promise}
*/
async prepareClassPathData(owner, ownerIdProperty, classPathsListById, skillsClassesList)
{
// @TODO - BETA - Temporal one class path per player, we will have optional multiple classes.
let currentPlayerClassPath = await this.loadOwnerClassPath(owner[ownerIdProperty]);
if(!currentPlayerClassPath){
Logger.error(['Undefined class path for player.', 'ID:', owner[ownerIdProperty]]);
return false;
}
let currentClassPath = classPathsListById[currentPlayerClassPath.class_path_id];
//Logger.debug('Current class path:', currentClassPath);
let skillsByLevel = SkillsGenerator.skillsByLevelsFromSkillsModels(
currentClassPath.data.related_skills_class_path_level_skills,
owner,
ownerIdProperty,
skillsClassesList,
this.events
);
//Logger.debug('Current Class Path:', currentClassPath);
return {
key: currentClassPath.data.key,
label: currentClassPath.data.label,
owner,
ownerIdProperty,
levels: currentClassPath.data.classPathLevels,
labelsByLevel: currentClassPath.data.labelsByLevel,
skillsByLevel,
autoFillRanges: currentClassPath.data.related_skills_levels_set.autoFillRanges,
autoSortLevels: currentClassPath.data.related_skills_levels_set.autoSortLevels,
currentLevel: currentPlayerClassPath.currentLevel,
currentExp: currentPlayerClassPath.currentExp,
};
}
}
module.exports.ModelsManager = ModelsManager;
================================================
FILE: lib/actions/server/player-class-path-handler.js
================================================
/**
*
* Reldens - PlayerClassPathHandler
*
* Creates player class path assignments during login.
*
*/
const { sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
*/
class PlayerClassPathHandler
{
/**
* @param {Object} props
* @param {Object} props.loginManager
* @param {Object} props.loginData
* @param {Object} props.player
* @param {BaseDataServer} props.dataServer
* @returns {Promise}
*/
static async createFromLoginData(props)
{
let {loginManager, loginData, player, dataServer} = props;
let defaultClassPathId = loginManager.config.get('server/players/actions/initialClassPathId');
let initialClassPathId = sc.get(loginData, 'class_path_select', defaultClassPathId);
let data = {
class_path_id: initialClassPathId,
owner_id: player.id,
currentLevel: 1,
currentExp: 0
};
return dataServer.getEntity('skillsOwnersClassPath').create(data);
}
}
module.exports.PlayerClassPathHandler = PlayerClassPathHandler;
================================================
FILE: lib/actions/server/player-enricher.js
================================================
/**
*
* Reldens - PlayerEnricher
*
* Enriches players with skills, class paths, and combat capabilities.
*
*/
const { Pvp } = require('./pvp');
const { SkillsExtraDataMapper } = require('./skills-extra-data-mapper');
const { ClientWrapper } = require('../../game/server/client-wrapper');
const SkillsServer = require('@reldens/skills/lib/server');
const { StorageObserver } = require('./storage-observer');
const { SkillConst } = require('@reldens/skills');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('../../users/server/player').Player} Player
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
* @typedef {import('./models-manager').ModelsManager} ModelsManager
*
* @typedef {Object} PlayerEnricherProps
* @property {ConfigManager} config
* @property {EventsManager} events
* @property {ModelsManager} skillsModelsManager
*/
class PlayerEnricher
{
/**
* @param {PlayerEnricherProps} props
*/
constructor(props)
{
/** @type {ConfigManager} */
this.config = props.config;
/** @type {EventsManager} */
this.events = props.events;
/** @type {ModelsManager} */
this.skillsModelsManager = props.skillsModelsManager;
/** @type {boolean} */
this.pvpEnabled = this.config.getWithoutLogs('server/actions/pvp/enabled', true);
/** @type {Object} */
this.pvpConfig = Object.assign({events: this.events}, this.config.get('server/actions/pvp'));
/** @type {string} */
this.affectedProperty = this.config.get('client/actions/skills/affectedProperty');
/** @type {SkillsExtraDataMapper} */
this.skillsExtraDataMapper = new SkillsExtraDataMapper();
}
/**
* @param {RoomScene} roomGame
* @param {Object} superInitialGameData
* @returns {Promise}
*/
async withClassPath(roomGame, superInitialGameData)
{
//Logger.debug('Players Models:', superInitialGameData.players);
if(!superInitialGameData.players){
return;
}
for(let i of Object.keys(superInitialGameData.players)){
let player = superInitialGameData.players[i];
let classPath = await this.skillsModelsManager.loadOwnerClassPath(player.id);
if(!classPath){
continue;
}
player.currentLevel = classPath.currentLevel;
player.currentClassPathLabel = classPath.related_skills_class_path.label;
player.currentClassPathKey = player.avatarKey = classPath.related_skills_class_path.key;
}
}
/**
* @param {Player} currentPlayer
* @param {RoomScene} room
* @returns {Promise}
*/
async withActions(currentPlayer, room)
{
currentPlayer.actions = {};
if(this.pvpEnabled && false !== sc.get(room.customData, 'pvpEnabled', true)){
currentPlayer.actions['pvp'] = new Pvp(this.pvpConfig);
}
currentPlayer.getSkillExtraData = (params) => {
return this.skillsExtraDataMapper.extractSkillExtraData(params);
};
currentPlayer.executePhysicalSkill = this.playerExecutePhysicalSkillCallback(
currentPlayer,
room.config.client.skills.animations
);
}
/**
* @param {Player} currentPlayer
* @param {Object} skillsAnimationsData
* @returns {Function}
*/
playerExecutePhysicalSkillCallback(currentPlayer, skillsAnimationsData)
{
// @TODO - BETA - Replace with bind.
return async (target, executedSkill) => {
let messageData = Object.assign({skillKey: executedSkill.key}, executedSkill.owner.getPosition());
if(sc.isObjectFunction(executedSkill.owner, 'getSkillExtraData')){
let params = {skill: executedSkill, target};
Object.assign(messageData, {extraData: executedSkill.owner.getSkillExtraData(params)});
}
await currentPlayer.skillsServer.client.runBehaviors(
messageData,
SkillConst.ACTION_SKILL_AFTER_CAST,
SkillConst.BEHAVIOR_BROADCAST,
executedSkill.getOwnerId()
);
let from = {x: currentPlayer.state.x, y: currentPlayer.state.y};
executedSkill.initialPosition = from;
let animData = sc.get(skillsAnimationsData, executedSkill.key + '_bullet', false);
if(animData){
executedSkill.animDir = sc.get(animData.animationData, 'dir', false);
}
// player disconnection would cause the physicalBody to be removed, so we need to validate it:
let physicalBody = currentPlayer.physicalBody;
if(!physicalBody){
Logger.info('Player body is missing.');
return false;
}
if(!physicalBody.world){
Logger.error('Player body world is missing. Body ID: '+ physicalBody.id);
return false;
}
physicalBody.world.shootBullet(from, {x: target.state.x, y: target.state.y}, executedSkill);
};
}
/**
* @param {Object} props
* @param {Object} props.client
* @param {Player} props.currentPlayer
* @param {RoomScene} props.room
* @param {ModelsManager} props.skillsModelsManager
* @param {BaseDataServer} props.dataServer
* @param {EventsManager} props.events
* @returns {Promise}
*/
async withSkillsServerAndClassPath(props)
{
let {client, currentPlayer, room, skillsModelsManager, dataServer, events} = props;
// @TODO - BETA - Improve prepareClassPathData to avoid loadOwnerClassPath double queries on each room.
let classPathData = await skillsModelsManager.prepareClassPathData(
currentPlayer,
'player_id',
room.config.skills.classPaths.classPathsById,
room.config.skills.skillsList
);
if(!classPathData){
return;
}
Object.assign(classPathData, {
events: events,
persistence: true,
dataServer: dataServer,
affectedProperty: this.affectedProperty,
client: new ClientWrapper({client, room})
});
currentPlayer.skillsServer = new SkillsServer(classPathData);
this.storageObserver = new StorageObserver({
classPath: currentPlayer.skillsServer.classPath,
dataServer: dataServer,
modelsManager: skillsModelsManager,
});
this.storageObserver.registerListeners();
currentPlayer.avatarKey = classPathData.key;
}
}
module.exports.PlayerEnricher = PlayerEnricher;
================================================
FILE: lib/actions/server/plugin.js
================================================
/**
*
* Reldens - ActionsPlugin
*
* Plugin that integrates the actions/skills system into the game server.
*
*/
const { ActionsMessageActions } = require('./message-actions');
const { PluginInterface } = require('../../features/plugin-interface');
const { InitialGameDataEnricher } = require('./initial-game-data-enricher');
const { PlayerEnricher } = require('./player-enricher');
const { DataLoader } = require('./data-loader');
const { EventListeners } = require('./event-listeners');
const { PlayerClassPathHandler } = require('./player-class-path-handler');
const { ModelsManager } = require('./models-manager');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../users/server/player').Player} Player
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
*/
class ActionsPlugin extends PluginInterface
{
/**
* @param {Object} props
*/
setup(props)
{
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in ActionsPlugin.');
}
this.dataServer = sc.get(props, 'dataServer', false);
if(!this.dataServer){
Logger.error('Data Server undefined in ActionsPlugin.');
}
this.config = sc.get(props, 'config', false);
if(!this.config){
Logger.error('Config undefined in ActionsPlugin.');
}
this.skillsModelsManager = new ModelsManager({events: this.events, dataServer: this.dataServer});
this.playerEnricher = new PlayerEnricher({
events: this.events,
config: this.config,
skillsModelsManager: this.skillsModelsManager
});
this.initialGameDataEnricher = new InitialGameDataEnricher();
this.listenEvents();
}
/**
* @returns {boolean|void}
*/
listenEvents()
{
if(!this.events){
return false;
}
this.events.on('reldens.serverReady', this.serverReadyDataLoaderEnrichConfig.bind(this));
this.events.on('reldens.beforeSuperInitialGameData', this.enrichInitialGameDataWithClassPathData.bind(this));
this.events.on('reldens.roomsMessageActionsByRoom', this.appendRoomActions.bind(this));
this.events.on('reldens.createdPlayerSchema', this.enrichPlayerWithSkillsAndActions.bind(this));
this.events.on('reldens.createdNewPlayer', this.createPlayerClassPath.bind(this));
}
/**
* @param {Object} event
* @returns {Promise}
*/
async serverReadyDataLoaderEnrichConfig(event)
{
await DataLoader.enrichConfig(event.serverManager.configManager, this.skillsModelsManager, this.dataServer);
}
/**
* @param {Object} superInitialGameData
* @param {RoomScene} roomGame
* @returns {Promise}
*/
async enrichInitialGameDataWithClassPathData(superInitialGameData, roomGame)
{
await this.initialGameDataEnricher.withClassPathLabels(roomGame, superInitialGameData);
await this.playerEnricher.withClassPath(roomGame, superInitialGameData);
}
/**
* @param {RoomScene} roomMessageActions
* @returns {Promise}
*/
async appendRoomActions(roomMessageActions)
{
roomMessageActions.actions = new ActionsMessageActions();
}
/**
* @param {Object} client
* @param {Object} userModel
* @param {Player} currentPlayer
* @param {RoomScene} room
* @returns {Promise}
*/
async enrichPlayerWithSkillsAndActions(client, userModel, currentPlayer, room)
{
await this.playerEnricher.withActions(currentPlayer, room, this.events);
// @TODO - BETA - Improve login performance.
await this.playerEnricher.withSkillsServerAndClassPath({
client,
room,
skillsModelsManager: this.skillsModelsManager,
currentPlayer,
dataServer: this.dataServer,
events: this.events
});
await EventListeners.attachCastMovementEvents({
classPath: currentPlayer.skillsServer.classPath,
events: this.events,
actionsPlugin: this
});
}
/**
* @param {Player} player
* @param {Object} loginData
* @param {Object} loginManager
* @returns {Promise}
*/
async createPlayerClassPath(player, loginData, loginManager)
{
return PlayerClassPathHandler.createFromLoginData({
loginManager,
loginData,
player,
dataServer: this.dataServer
});
}
}
module.exports.ActionsPlugin = ActionsPlugin;
================================================
FILE: lib/actions/server/pve.js
================================================
/**
*
* Reldens - Pve
*
* Handles player versus environment (NPC/enemy) combat logic.
*
*/
const { Battle } = require('./battle');
const { BattleEndAction } = require('./battle-end-action');
const { BattleEndedEvent } = require('./events/battle-ended-event');
const { GameConst } = require('../../game/constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../users/server/player').Player} Player
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
*/
class Pve extends Battle
{
/**
* @param {Object} props
*/
constructor(props)
{
super(props);
/** @type {boolean} */
this.chaseMultiple = sc.get(props, 'chaseMultiple', false);
/** @type {Object} */
this.inBattleWithPlayers = {};
/** @type {boolean} */
this.isBattleEndProcessing = false;
/** @type {string} */
this.uid = sc.randomChars(8)+'-'+(new Date()).getTime();
}
/**
* @param {Object} targetObject
*/
setTargetObject(targetObject)
{
this.targetObject = targetObject;
}
/**
* @param {Player} playerSchema
* @param {Object} target
* @param {RoomScene} roomScene
* @returns {Promise}
*/
async runBattle(playerSchema, target, roomScene)
{
//Logger.debug('Running Battle between "'+playerSchema.sessionId+'" and "'+target.id+'".', this.uid);
if(GameConst.STATUS.ACTIVE !== playerSchema.state.inState){
//Logger.debug('PvE inactive player.', playerSchema.state.inState);
delete this.inBattleWith[target.id];
return false;
}
if(!this.targetObject){
// @NOTE: this will be the expected case when the player was killed in between different NPCs attacks.
// Logger.debug('Target Object reference removed.');
return false;
}
let affectedProperty = roomScene.config.get('client/actions/skills/affectedProperty');
// @TODO - BETA - Target affected property could be passed on the target object.
if(!affectedProperty){
Logger.error('Affected property configuration is missing');
return false;
}
//Logger.debug('Run Battle - Object:', this.targetObject?.uid, this.targetObject.stats[affectedProperty]);
if(0 >= this.targetObject.stats[affectedProperty]){
// Logger.debug('Target object affected property is zero.');
await this.battleEnded(playerSchema, roomScene);
return false;
}
// @TODO - BETA - Make PvP available by configuration.
// @NOTE: run battle method is for when the player attacks any target. PVE can be started in different ways,
// depending on how the current enemy-object was implemented, for example the PVE can start when the player just
// collides with the enemy (instead of attack it) an aggressive enemy could start the battle automatically.
let attackResult = await super.runBattle(playerSchema, target, roomScene);
await this.events.emit('reldens.runBattlePveAfter', {playerSchema, target, roomScene, attackResult});
if(!attackResult){
// @NOTE: the attack result can be false because different reasons, for example it could be a physical
// attack for which matter we won't start the battle until the physical body hits the target.
return false;
}
if(!sc.hasOwn(target.stats, affectedProperty)){
Logger.error('Affected property is not present on target stats.', Object.keys(target.stats));
return false;
}
let affectedPropertyTargetValue = Number(sc.get(target.stats, affectedProperty, 0));
if(0 < affectedPropertyTargetValue){
return await this.startBattleWith(playerSchema, roomScene);
}
// physical attacks or effects will run the battleEnded, normal attacks or effects will hit this case:
return await this.battleEnded(playerSchema, roomScene);
}
/**
* @param {Player} playerSchema
* @param {RoomScene} room
* @returns {Promise}
*/
async startBattleWith(playerSchema, room)
{
//Logger.debug('Starts PvE', playerSchema?.player_id, this.targetObject?.uid);
if(!this.targetObject){
// @NOTE: this will be the expected case when the player was killed in between different NPCs attacks.
// Logger.debug('Target Object reference removed.');
return false;
}
let affectedProperty = room.config.get('client/actions/skills/affectedProperty');
// Logger.debug('Start Battle - Object:', this.targetObject?.uid, this.targetObject.stats[affectedProperty]);
if(0 >= this.targetObject.stats[affectedProperty]){
// Logger.debug('Target object affected property is zero.');
return false;
}
if(0 === (this.targetObject.actionsKeys?.length ?? 0)){
Logger.warning('Target Object does not have any actions assigned.');
this.leaveBattle(playerSchema);
return false;
}
let targetObjectWorld = this.targetObject.objectBody?.world;
let objectWorldKey = targetObjectWorld?.worldKey;
let playerWorld = playerSchema?.physicalBody?.world;
let playerWorldKey = playerWorld?.worldKey;
if(!objectWorldKey || !playerWorldKey || objectWorldKey !== playerWorldKey){
if(playerWorldKey){
Logger.debug('World keys check failed:', playerWorld.sceneName, {objectWorldKey, playerWorldKey});
}
// playerWorldKey will be null while the player is dead because the removeBody
// Logger.debug('Leaving battle, world keys check failed.', playerSchema.player_id);
this.leaveBattle(playerSchema);
return false;
}
if(
!room?.roomWorld
|| !room?.state
|| !playerSchema
|| !room.playerBySessionIdFromState(playerSchema.sessionId)
){
//Logger.debug('Room or player missing references.');
// @NOTE: leaveBattle is used for when the player can't be reached anymore or disconnected.
this.leaveBattle(playerSchema);
return false;
}
if(!playerSchema.stats){
Logger.debug('Player not ready yet to be attacked.');
this.leaveBattle(playerSchema);
return false;
}
// if target (npc) is already in battle with another player then ignore the current attack:
let inBattleWithPlayersIds = Object.keys(this.inBattleWithPlayers);
let inBattleWithCurrentPlayer = this.inBattleWithPlayers[playerSchema.player_id];
if(!this.chaseMultiple && 1 <= inBattleWithPlayersIds.length && !inBattleWithCurrentPlayer){
Logger.debug('Object already in battle with player "'+playerSchema.player_id+'".');
return false;
}
this.inBattleWithPlayers[playerSchema.player_id] = true;
let objectAction = this.pickRandomActionFromObject();
objectAction.room = room;
objectAction.currentBattle = this;
if(!objectAction.validate()){
// @NOTE: none logs here because it will create a (useless) log entry everytime an NPC tries to attack.
// expected when for example the action is out of range or has a skill delay, then we restart the battle:
return this.chasePlayer(playerSchema, room, objectAction);
}
let ownerPos = {x: this.targetObject.state.x, y: this.targetObject.state.y};
let targetPos = {x: playerSchema.state.x, y: playerSchema.state.y};
let inRange = objectAction.isInRange(ownerPos, targetPos);
if(inRange){
this.isBattleEndProcessing = false;
return await this.attackInRange(objectAction, playerSchema, room);
}
return this.chasePlayer(playerSchema, room, objectAction);
}
/**
* @returns {Object}
*/
pickRandomActionFromObject()
{
let objActionIdx = Math.floor(Math.random() * this.targetObject.actionsKeys.length);
let objectActionKey = this.targetObject.actionsKeys[objActionIdx];
return this.targetObject.actions[objectActionKey];
}
/**
* @param {Player} playerSchema
* @param {RoomScene} room
* @param {Object} objectAction
* @returns {boolean|Promise}
*/
chasePlayer(playerSchema, room, objectAction)
{
let chaseResult = this.targetObject.chaseBody(playerSchema.physicalBody);
if(chaseResult && 0 < chaseResult.length){
return this.startBattleWithDelay(playerSchema, room, objectAction);
}
Logger.debug('Leave battle, chase result failed.');
return this.leaveBattle(playerSchema);
}
/**
* @param {Object} objectAction
* @param {Player} playerSchema
* @param {RoomScene} room
* @returns {Promise>}
*/
async attackInRange(objectAction, playerSchema, room)
{
if(this.targetObject.objectBody){
// reset the pathfinder in case the object was moving:
this.targetObject.objectBody.resetAuto();
this.targetObject.objectBody.velocity = [0, 0];
}
// execute and apply the attack:
await objectAction.execute(playerSchema);
Logger.debug('Executed action "'+objectAction.key+'" on player "'+playerSchema.player_id+'".');
let targetClient = room.getClientById(playerSchema.sessionId);
if(!targetClient){
Logger.debug('Leave battle, missing target client.');
return this.leaveBattle(playerSchema);
}
let targetObjectId = this.targetObject?.id;
if(!targetObjectId){
Logger.debug('Leave battle, missing target object ID.');
return this.leaveBattle(playerSchema);
}
let update = await this.updateTargetClient(targetClient, playerSchema, targetObjectId, room)
.catch((error) => {
Logger.error('Leave battle, update target client catch error.', error);
return this.leaveBattle(playerSchema);
});
if(update){
return await this.startBattleWithDelay(playerSchema, room, objectAction);
}
Logger.debug('Leave battle, target client update failed.');
return this.leaveBattle(playerSchema);
}
/**
* @param {Player} playerSchema
* @param {RoomScene} room
* @param {Object} objectAction
* @returns {Promise}
*/
async startBattleWithDelay(playerSchema, room, objectAction)
{
if(0 < objectAction.skillDelay){
setTimeout(async () => {
if(!this.targetObject){
return false;
}
await this.startBattleWith(playerSchema, room);
}, objectAction.skillDelay);
return;
}
await this.startBattleWith(playerSchema, room);
}
/**
* @param {Player} playerSchema
* @returns {boolean}
*/
leaveBattle(playerSchema)
{
Logger.debug('Leaving battle.', {player: playerSchema?.player_id, object: this.targetObject?.uid});
if(playerSchema?.player_id){
this.removeInBattlePlayer(playerSchema);
}
return this.moveObjectToOriginPoints();
}
/**
* @returns {boolean}
*/
moveObjectToOriginPoints()
{
if(!this.targetObject){
// expected on client disconnection:
// Logger.debug('Target Object reference not found.');
return false;
}
if(GameConst.STATUS.ACTIVE !== this.targetObject.objectBody.bodyState.inState){
return false;
}
Logger.debug('Move back to origin.', {
uid: this.uid,
object: this.targetObject.uid,
state: this.targetObject.objectBody.bodyState.inState,
column: this.targetObject.objectBody.originalCol,
row: this.targetObject.objectBody.originalRow
});
this.targetObject.objectBody.moveToOriginalPoint();
return true;
}
/**
* @param {Player} playerSchema
* @param {RoomScene} room
* @returns {Promise}
*/
async battleEnded(playerSchema, room)
{
if(this.isBattleEndProcessing){
Logger.debug('Battle end in progress.', this.uid);
return false;
}
this.isBattleEndProcessing = true;
// @TODO - BETA - Implement battle end in both PvE and PvP.
this.targetObject.objectBody.bodyState.inState = GameConst.STATUS.DEATH;
Logger.debug(
'Battle end, player ID "'+playerSchema?.player_id+'", target ID "'+this.targetObject.uid+'".',
this.uid
);
this.removeInBattlePlayer(playerSchema);
let actionData = new BattleEndAction(
this.targetObject.objectBody.position[0],
this.targetObject.objectBody.position[1],
this.targetObject.key,
this.lastAttackKey
);
room.broadcast('*', actionData);
if(sc.isObjectFunction(this.targetObject, 'respawn')){
await this.targetObject.respawn(room);
}
this.sendBattleEndedActionData(room, playerSchema, actionData);
let event = new BattleEndedEvent({playerSchema, pve: this, actionData, room});
await this.events.emit(this.targetObject.getBattleEndEvent(), event);
await this.events.emit('reldens.battleEnded', event);
return true;
}
/**
* @param {RoomScene} room
* @param {Player} playerSchema
* @param {BattleEndAction} actionData
*/
sendBattleEndedActionData(room, playerSchema, actionData)
{
let client = room.getClientById(playerSchema.sessionId);
if(!client){
Logger.info('Client not found by sessionId: '+ playerSchema.sessionId);
return;
}
client.send('*', actionData);
}
/**
* @param {Player} playerSchema
* @returns {boolean}
*/
removeInBattlePlayer(playerSchema)
{
if(!playerSchema?.player_id){
return false;
}
if(this.inBattleWithPlayers[playerSchema.player_id]){
delete this.inBattleWithPlayers[playerSchema.player_id];
}
return true;
}
}
module.exports.Pve = Pve;
================================================
FILE: lib/actions/server/pvp.js
================================================
/**
*
* Reldens - PvP
*
* Handles player versus player combat logic.
*
*/
const { Battle } = require('./battle');
const { Logger } = require('@reldens/utils');
const { GameConst } = require('../../game/constants');
/**
* @typedef {import('../../users/server/player').Player} Player
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
*/
class Pvp extends Battle
{
/**
* @param {Player} playerSchema
* @param {Player} target
* @param {RoomScene} room
* @returns {Promise}
*/
async runBattle(playerSchema, target, room)
{
// @TODO - BETA - Implement battle end for PvP.
if(GameConst.STATUS.ACTIVE !== playerSchema.state.inState){
Logger.info('PvP inactive player.', playerSchema.state.inState);
return false;
}
if(GameConst.STATUS.ACTIVE !== target.state.inState){
Logger.info('PvP inactive target.', target.state.inState);
return false;
}
// @TODO - BETA - Make PvP available by configuration.
// can't fight with yourself:
if(playerSchema.sessionId === target.sessionId){
await this.executeAction(playerSchema, target);
return false;
}
// @NOTE: run battle method is for when the player attacks a target.
let inBattle = await super.runBattle(playerSchema, target, room);
if(!inBattle){
return false;
}
let targetClient = room.getClientById(target.sessionId);
if(targetClient){
await this.updateTargetClient(targetClient, target, playerSchema.sessionId, room, playerSchema);
}
return true;
}
/**
* @param {Player} playerSchema
* @param {Player} target
* @returns {Promise}
*/
async executeAction(playerSchema, target)
{
let currentAction = this.getCurrentAction(playerSchema);
if(!currentAction){
Logger.error('Actions not defined for this player. ID: '+playerSchema.player_id);
return false;
}
// @TODO - BETA - Move self target validation to skills npm package.
if(!currentAction.allowSelfTarget){
return false;
}
currentAction.currentBattle = this;
await currentAction.execute(target);
return false;
}
}
module.exports.Pvp = Pvp;
================================================
FILE: lib/actions/server/skills/type-attack.js
================================================
/**
*
* Reldens - TypeAttack
*
* Handles attack-type skills with room broadcast and battle initiation.
*
*/
const { Attack } = require('@reldens/skills');
const { sc } = require('@reldens/utils');
class TypeAttack extends Attack
{
/**
* @param {Object} props
*/
constructor(props)
{
super(props);
/** @type {any|false} */
this.room = false;
/** @type {any|false} */
this.currentBattle = false;
}
/**
* @returns {Promise}
*/
async runSkillLogic()
{
if(this.room){
// @TODO - BETA - Replace all the defaults by constants.
let skillAction = this.key+'_atk';
this.room.broadcast('*', {
act: skillAction,
owner: this.owner.broadcastKey,
target: this.target.broadcastKey
});
}
await super.runSkillLogic();
if(
sc.hasOwn(this.owner, 'player_id')
&& sc.hasOwn(this.target, 'objectBody')
&& this.currentBattle
&& 0 < this.getAffectedPropertyValue(this.target)
){
await this.currentBattle.startBattleWith(this.owner, this.room);
}
return true;
}
}
module.exports = TypeAttack;
================================================
FILE: lib/actions/server/skills/type-effect.js
================================================
/**
*
* Reldens - TypeEffect
*
* Handles effect-type skills with room broadcast and battle initiation.
*
*/
const { Effect } = require('@reldens/skills');
const { sc } = require('@reldens/utils');
class TypeEffect extends Effect
{
/**
* @param {Object} props
*/
constructor(props)
{
super(props);
/** @type {any|false} */
this.room = false;
/** @type {any|false} */
this.currentBattle = false;
}
/**
* @returns {Promise}
*/
async runSkillLogic()
{
if(this.room){
// @TODO - BETA - Replace all the defaults by constants.
let skillAction = this.key+'_eff';
this.room.broadcast('*', {
act: skillAction,
owner: this.owner.broadcastKey,
target: this.target.broadcastKey
});
}
await super.runSkillLogic();
if(sc.hasOwn(this.owner, 'player_id') && sc.hasOwn(this.target, 'objectBody') && this.currentBattle){
await this.currentBattle.startBattleWith(this.owner, this.room);
}
return true;
}
}
module.exports = TypeEffect;
================================================
FILE: lib/actions/server/skills/type-physical-attack.js
================================================
/**
*
* Reldens - TypePhysicalAttack
*
* Handles physical attack skills with collision detection, bullets, and PvE/PvP logic.
*
*/
const { PhysicalAttack } = require('@reldens/skills');
const { sc } = require('@reldens/utils');
class TypePhysicalAttack extends PhysicalAttack
{
/**
* @param {Object} props
*/
constructor(props)
{
super(props);
/** @type {any|false} */
this.room = false;
/** @type {any|false} */
this.currentBattle = false;
/** @type {number} */
this.hitPriority = sc.get(props, 'hitPriority', 2);
/** @type {any|false} */
this.animDir = sc.get(props, 'animDir', false);
}
/**
* @param {Object} props
* @returns {Promise}
*/
async onHit(props)
{
// run bullets hit:
let bulletsCheck = this.executeBullets(props);
// if we have 0 bullets or both bodies are bullets, then we can skip the bullet hit check:
if(1 !== bulletsCheck.length){
// @TODO - BETA - Implement bullets bodies without collisions between each other.
return false;
}
let notTheBullet = 'body'+(bulletsCheck.shift().key === 'A' ? 'B' : 'A');
// get and validate the defender which could be a player or an object:
let validDefender = this.getValidDefender(props, notTheBullet);
if(!validDefender){
return false;
}
// run battle damage:
await super.executeOnHit(validDefender);
if(!validDefender?.state){
// Logger.info('Invalid defender, none State.', {key: validDefender?.key});
return false;
}
let hitKey = this.key+'_hit';
let hitMessage = {
act: hitKey,
x: validDefender.state.x,
y: validDefender.state.y,
owner: this.owner.broadcastKey,
target: validDefender.broadcastKey
};
this.room.broadcast('*', hitMessage);
if(sc.hasOwn(this.owner, 'player_id') && sc.hasOwn(validDefender, 'objectBody') && this.currentBattle){
return await this.startPvE(validDefender);
}
return await this.sendUpdateFromPvP(validDefender);
}
/**
* @param {Object} validDefender
* @returns {Promise}
*/
async sendUpdateFromPvP(validDefender)
{
// update the clients if pvp:
if(!sc.hasOwn(validDefender, 'player_id')){
return false;
}
let targetClient = this.room.getClientById(validDefender.broadcastKey);
if(!targetClient){
return false;
}
await this.currentBattle.updateTargetClient(
targetClient,
validDefender,
this.owner.sessionId,
this.room,
this.owner
);
}
/**
* @param {Object} validDefender
* @returns {Promise}
*/
async startPvE(validDefender)
{
if(0 < validDefender.stats[this.room.config.get('client/actions/skills/affectedProperty')]){
return await this.restartBattle(validDefender);
}
return await this.currentBattle.battleEnded(this.owner, this.room);
}
/**
* @param {Object} validDefender
* @returns {Promise}
*/
async restartBattle(validDefender)
{
if(!this.validateTargetOnHit && sc.hasOwn(validDefender, 'battle')){
if(!validDefender.battle){
return;
}
// if target validation is disabled then any target could start the battle (pve):
validDefender.battle.targetObject = validDefender;
await validDefender.battle.startBattleWith(this.owner, this.room);
return;
}
// if target validation is enabled, then we can only start the battle with the target:
await this.currentBattle.startBattleWith(this.owner, this.room);
}
/**
* @param {Object} props
* @returns {Array}
*/
executeBullets(props)
{
let bulletsCheck = [];
// @TODO - BETA - Replace all the defaults by constants.
// both objects could be bullets, so remove them is needed and broadcast the hit:
if(props.bodyA.isBullet){
this.removeBullet(props.bodyA);
bulletsCheck.push({key: 'bodyA', obj: props.bodyA});
}
if(props.bodyB.isBullet){
this.removeBullet(props.bodyB);
bulletsCheck.push({key: 'bodyB', obj: props.bodyB});
}
return bulletsCheck;
}
/**
* @param {Object} props
* @param {string} defenderBodyKey
* @returns {Object}
*/
getValidDefender(props, defenderBodyKey)
{
// we already validate if one of the bodies is a bullet so the other will be always a player or an object:
let playerId = sc.get(props[defenderBodyKey], 'playerId', null);
if(null !== playerId){
return this.room.playerBySessionIdFromState(playerId);
}
return props[defenderBodyKey].roomObject;
}
/**
* @param {Object} body
*/
removeBullet(body)
{
if(body.world){
body.world.removeBodies.push(body);
}
this.room.state.removeBody(this.key+'_bullet_'+body.id);
}
}
module.exports = TypePhysicalAttack;
================================================
FILE: lib/actions/server/skills/type-physical-effect.js
================================================
/**
*
* Reldens - TypePhysicalEffect
*
* Handles physical effect skills with collision detection, bullets, and PvE/PvP logic.
*
*/
const { PhysicalEffect } = require('@reldens/skills');
const { sc } = require('@reldens/utils');
class TypePhysicalEffect extends PhysicalEffect
{
/**
* @param {Object} props
*/
constructor(props)
{
super(props);
/** @type {any|false} */
this.room = false;
/** @type {any|false} */
this.currentBattle = false;
/** @type {number} */
this.hitPriority = sc.get(props, 'hitPriority', 2);
/** @type {any|false} */
this.animDir = sc.get(props, 'animDir', false);
}
/**
* @param {Object} props
* @returns {Promise}
*/
async onHit(props)
{
// run bullets hit:
let bulletsCheck = this.executeBullets(props);
let notTheBullet = bulletsCheck[0].key === 'bodyA' ? 'bodyB' : 'bodyA';
// none bullets or both bullets:
if(bulletsCheck.length !== 1){
// @TODO - BETA - Implement bullets bodies without collisions between each other.
return false;
}
// get and validate a defender which could be a player or an object:
let validDefender = this.getValidDefender(props, notTheBullet);
if(!validDefender){
return false;
}
// run battle damage:
await super.executeOnHit(validDefender);
let hitKey = this.key+'_hit';
this.room.broadcast('*', {
act: hitKey,
x: notTheBullet.position[0],
y: notTheBullet.position[1],
owner: this.owner.broadcastKey,
target: validDefender.broadcastKey
});
(sc.hasOwn(this.owner, 'player_id') && sc.hasOwn(validDefender, 'objectBody') && this.currentBattle)
? await this.startPvE(validDefender)
: await this.sendUpdateFromPvP(validDefender);
return false;
}
/**
* @param {Object} validDefender
* @returns {Promise}
*/
async sendUpdateFromPvP(validDefender)
{
if(!sc.hasOwn(validDefender, 'player_id')){
return false;
}
let targetClient = this.room.getClientById(validDefender.broadcastKey);
if(!targetClient){
return false;
}
await this.currentBattle.updateTargetClient(
targetClient,
validDefender,
this.owner.sessionId,
this.room,
this.owner
);
}
/**
* @param {Object} validDefender
* @returns {Promise}
*/
async startPvE(validDefender)
{
if(0 < validDefender.stats[this.room.config.get('client/actions/skills/affectedProperty')]){
return await this.restartBattle(validDefender);
}
return await this.currentBattle.battleEnded(this.owner, this.room);
}
/**
* @param {Object} validDefender
* @returns {Promise}
*/
async restartBattle(validDefender)
{
if(!this.validateTargetOnHit && sc.hasOwn(validDefender, 'battle')){
if(!validDefender.battle){
return;
}
// if target validation is disabled, then any target could start the battle (pve):
validDefender.battle.targetObject = validDefender;
await validDefender.battle.startBattleWith(this.owner, this.room);
return;
}
// if target validation is enabled then we can only start the battle with the target:
await this.currentBattle.startBattleWith(this.owner, this.room);
}
/**
* @param {Object} props
* @returns {Array}
*/
executeBullets(props)
{
let bulletsCheck = [];
// @TODO - BETA - Replace all the defaults by constants.
// both objects could be bullets, so remove them is needed and broadcast the hit:
if(props.bodyA.isBullet){
this.removeBullet(props.bodyA);
bulletsCheck.push({key: 'bodyA', obj: props.bodyA});
}
if(props.bodyB.isBullet){
this.removeBullet(props.bodyB);
bulletsCheck.push({key: 'bodyB', obj: props.bodyB});
}
return bulletsCheck;
}
/**
* @param {Object} props
* @param {string} defenderBodyKey
* @returns {Object}
*/
getValidDefender(props, defenderBodyKey)
{
// we already validate if one of the bodies is a bullet, so the other will always be a player or an object:
return sc.hasOwn(props[defenderBodyKey], 'playerId') ?
this.room.playerBySessionIdFromState(props[defenderBodyKey].playerId) : props[defenderBodyKey].roomObject;
}
/**
* @param {Object} body
*/
removeBullet(body)
{
body.world.removeBodies.push(body);
// @TODO - BETA - Refactor and extract Colyseus into a driver. Check is been used?
this.room.state.removeBody(this.key+'_bullet_'+body.id);
}
}
module.exports = TypePhysicalEffect;
================================================
FILE: lib/actions/server/skills/types.js
================================================
/**
*
* Reldens - Export Skills Types
*
*/
module.exports = {
TypeAttack: require('./type-attack'),
TypeEffect: require('./type-effect'),
TypePhysicalAttack: require('./type-physical-attack'),
TypePhysicalEffect: require('./type-physical-effect')
};
================================================
FILE: lib/actions/server/skills-class-path-loader.js
================================================
/**
*
* Reldens - Skills - SkillsClassPathLoader
*
* Loads class path data with levels, skills, and their relationships.
*
*/
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
*
* @typedef {Object} SkillsClassPathLoaderProps
* @property {BaseDataServer} [dataServer]
*/
class SkillsClassPathLoader
{
/**
* @param {SkillsClassPathLoaderProps} props
*/
constructor(props)
{
/** @type {BaseDataServer|false} */
this.dataServer = sc.get(props, 'dataServer', false);
}
/**
* @param {string} entityName
* @returns {any|false}
*/
getEntity(entityName)
{
if(!entityName){
Logger.warning('Entity name is missing.');
return false;
}
if(!this.dataServer){
Logger.warning('Data server is missing.');
return false;
}
return this.dataServer.entityManager.get(entityName);
}
/**
* @returns {Promise}
*/
async loadFullPathData()
{
let classPathModels = await this.loadClassPathsWithLevelsSet();
if(!sc.isArray(classPathModels) || 0 === classPathModels.length){
return [];
}
let classPathIds = classPathModels.map(classPath => classPath.id);
let levelLabelsMap = await this.loadLevelLabelsByClassPathIds(classPathIds);
let levelSkillsMap = await this.loadLevelSkillsByClassPathIds(classPathIds);
let skillIds = this.extractSkillIds(levelSkillsMap);
let skillsMap = await this.loadSkillsWithRelations(skillIds);
return this.mapClassPathData(classPathModels, levelLabelsMap, levelSkillsMap, skillsMap);
}
/**
* @returns {Promise}
*/
async loadClassPathsWithLevelsSet()
{
return await this.getEntity('skillsClassPath').loadAllWithRelations([
'related_skills_levels_set.related_skills_levels.related_skills_levels_modifiers'
]);
}
/**
* @param {Array} classPathIds
* @returns {Promise}
*/
async loadLevelLabelsByClassPathIds(classPathIds)
{
let levelLabels = await this.getEntity('skillsClassPathLevelLabels').loadWithRelations(
{class_path_id: {operator: 'IN', value: classPathIds}},
['related_skills_levels']
);
return this.groupByClassPathId(levelLabels);
}
/**
* @param {Array} classPathIds
* @returns {Promise}
*/
async loadLevelSkillsByClassPathIds(classPathIds)
{
let levelSkills = await this.getEntity('skillsClassPathLevelSkills').loadWithRelations(
{class_path_id: {operator: 'IN', value: classPathIds}},
['related_skills_levels']
);
return this.groupByClassPathId(levelSkills);
}
/**
* @param {Object} levelSkillsMap
* @returns {Array}
*/
extractSkillIds(levelSkillsMap)
{
let skillIds = [];
let classPathIds = Object.keys(levelSkillsMap);
for(let classPathId of classPathIds){
let levelSkills = levelSkillsMap[classPathId];
for(let levelSkill of levelSkills){
if(-1 === skillIds.indexOf(levelSkill.skill_id)){
skillIds.push(levelSkill.skill_id);
}
}
}
return skillIds;
}
/**
* @param {Array} skillIds
* @returns {Promise}
*/
async loadSkillsWithRelations(skillIds)
{
if(0 === skillIds.length){
return {};
}
let skills = await this.getEntity('skillsSkill').loadWithRelations(
{id: {operator: 'IN', value: skillIds}},
[
'related_skills_skill_attack',
'related_skills_skill_physical_data',
'related_skills_skill_owner_conditions',
'related_skills_skill_owner_effects',
'related_skills_skill_target_effects'
]
);
return this.indexById(skills);
}
/**
* @param {Array} items
* @returns {Object}
*/
groupByClassPathId(items)
{
let result = {};
if(!sc.isArray(items)){
return result;
}
for(let item of items){
let classPathId = item.class_path_id;
if(!sc.hasOwn(result, classPathId)){
result[classPathId] = [];
}
result[classPathId].push(item);
}
return result;
}
/**
* @param {Array} items
* @returns {Object}
*/
indexById(items)
{
let result = {};
if(!sc.isArray(items)){
return result;
}
for(let item of items){
result[item.id] = item;
}
return result;
}
/**
* @param {Array} classPathModels
* @param {Object} levelLabelsMap
* @param {Object} levelSkillsMap
* @param {Object} skillsMap
* @returns {Array}
*/
mapClassPathData(classPathModels, levelLabelsMap, levelSkillsMap, skillsMap)
{
for(let classPath of classPathModels){
classPath.related_skills_class_path_level_labels = sc.get(levelLabelsMap, classPath.id, []);
let levelSkills = sc.get(levelSkillsMap, classPath.id, []);
this.attachSkillsToLevelSkills(levelSkills, skillsMap);
this.sortLevelSkillsBySkillKey(levelSkills);
classPath.related_skills_class_path_level_skills = levelSkills;
}
return classPathModels;
}
/**
* @param {Array} levelSkills
* @param {Object} skillsMap
*/
attachSkillsToLevelSkills(levelSkills, skillsMap)
{
for(let levelSkill of levelSkills){
levelSkill.related_skills_skill = sc.get(skillsMap, levelSkill.skill_id, null);
}
}
/**
* @param {Array} levelSkills
*/
sortLevelSkillsBySkillKey(levelSkills)
{
levelSkills.sort((a, b) => {
let keyA = sc.get(a, 'related_skills_skill.key', '');
let keyB = sc.get(b, 'related_skills_skill.key', '');
if(keyA < keyB){
return -1;
}
if(keyA > keyB){
return 1;
}
return 0;
});
}
}
module.exports.SkillsClassPathLoader = SkillsClassPathLoader;
================================================
FILE: lib/actions/server/skills-extra-data-mapper.js
================================================
/**
*
* Reldens - SkillsExtraDataMapper
*
* Maps skill data to extra data format for client synchronization.
*
*/
const { ActionsConst } = require('../constants');
const { TypeDeterminer } = require('../../game/type-determiner');
const { sc } = require('@reldens/utils');
/**
* @typedef {import('../../game/type-determiner').TypeDeterminer} TypeDeterminer
*/
class SkillsExtraDataMapper
{
constructor()
{
/** @type {TypeDeterminer} */
this.typeDeterminer = new TypeDeterminer();
}
/**
* @param {Object} params
* @returns {Object}
*/
extractSkillExtraData(params)
{
// @TODO - BETA - Refactor conditions.
let extraData = {};
let target = sc.get(params, 'target');
if(target){
if(this.typeDeterminer.isObject(target)){
extraData[ActionsConst.DATA_TARGET_TYPE] = ActionsConst.DATA_TYPE_VALUE_ENEMY;
extraData[ActionsConst.DATA_TARGET_KEY] = target.key;
}
if(this.typeDeterminer.isPlayer(target)){
extraData[ActionsConst.DATA_TARGET_TYPE] = ActionsConst.DATA_TYPE_VALUE_PLAYER;
extraData[ActionsConst.DATA_TARGET_KEY] = target.sessionId;
}
}
let skill = sc.get(params, 'skill');
if(skill){
if(this.typeDeterminer.isObject(skill.owner)){
extraData[ActionsConst.DATA_OWNER_TYPE] = ActionsConst.DATA_TYPE_VALUE_ENEMY;
extraData[ActionsConst.DATA_OWNER_KEY] = skill.owner.key;
}
if(this.typeDeterminer.isPlayer(skill.owner)){
extraData[ActionsConst.DATA_OWNER_TYPE] = ActionsConst.DATA_TYPE_VALUE_PLAYER;
extraData[ActionsConst.DATA_OWNER_KEY] = skill.owner.sessionId;
}
// @TODO - BETA - Check if we need to include any other skill data to be sent to the client.
if(0 < skill.skillDelay){
extraData[ActionsConst.EXTRA_DATA.SKILL_DELAY] = skill.skillDelay;
}
}
return extraData;
}
}
module.exports.SkillsExtraDataMapper = SkillsExtraDataMapper;
================================================
FILE: lib/actions/server/storage/class-path-generator.js
================================================
/**
*
* Reldens - Skills - ClassPathGenerator
*
* Generates class path data structures from database models.
*
*/
const { ClassPath } = require('@reldens/skills');
const { LevelsGenerator } = require('./levels-generator');
const { sc } = require('@reldens/utils');
class ClassPathGenerator
{
/**
* @param {Array} classPathModels
* @param {Object} classPathClasses
* @returns {Object}
*/
static fromClassPathModels(classPathModels, classPathClasses)
{
if(!sc.isArray(classPathModels) || 0 === classPathModels.length){
return {};
}
let classPathsById = {};
let classPathsByKey = {};
let activeClassPathModels = classPathModels.filter(classPathModel => classPathModel.enabled);
for(let classPathModel of activeClassPathModels){
let classPathData = {class: sc.get(classPathClasses, classPathModel.key, ClassPath), data: classPathModel};
classPathModel.classPathLevels = LevelsGenerator.fromLevelsModels(
classPathModel.related_skills_levels_set.related_skills_levels
);
classPathModel.labelsByLevel = this.extractLabelsByLevels(
classPathModel.related_skills_class_path_level_labels
);
classPathsById[classPathModel.id] = classPathData;
classPathsByKey[classPathModel.key] = classPathData;
}
return {classPathModels: activeClassPathModels, classPathsById, classPathsByKey};
}
/**
* @param {Array} levelLabelsModel
* @returns {Object}
*/
static extractLabelsByLevels(levelLabelsModel)
{
if(!sc.isArray(levelLabelsModel) || 0 === levelLabelsModel.length){
return {};
}
let labelsByLevel = {};
for(let labelData of levelLabelsModel){
labelsByLevel[labelData['related_skills_levels'].key] = labelData.label;
}
return labelsByLevel;
}
}
module.exports.ClassPathGenerator = ClassPathGenerator;
================================================
FILE: lib/actions/server/storage/conditions-generator.js
================================================
/**
*
* Reldens - Skills - ConditionsGenerator
*
* Generates condition instances from database models.
*
*/
const { Condition } = require('@reldens/modifiers');
const { sc } = require('@reldens/utils');
class ConditionsGenerator
{
/**
* @param {Array} conditionsModels
* @returns {Array}
*/
static fromConditionsModels(conditionsModels)
{
if(!sc.isArray(conditionsModels) || 0 === conditionsModels.length){
return [];
}
let conditions = [];
for(let conditionModel of conditionsModels){
conditionModel['propertyKey'] = conditionModel['property_key'];
let condition = new Condition(conditionModel);
conditions.push(condition);
}
return conditions;
}
}
module.exports.ConditionsGenerator = ConditionsGenerator;
================================================
FILE: lib/actions/server/storage/levels-generator.js
================================================
/**
*
* Reldens - Skills - LevelsGenerator
*
* Generates level instances from database models.
*
*/
const { Level } = require('@reldens/skills');
const { Modifier } = require('@reldens/modifiers');
const { sc } = require('@reldens/utils');
class LevelsGenerator
{
/**
* @param {Array} levelsModels
* @returns {Object}
*/
static fromLevelsModels(levelsModels)
{
let levels = {};
for(let levelData of levelsModels){
levelData.modifiers = this.extractModifiers(levelData['related_skills_levels_modifiers']);
let levelKey = Number(levelData['key']);
levelData.key = levelKey;
levels[levelKey] = new Level(levelData);
}
return levels;
}
/**
* @param {Array} modifiersModels
* @returns {Array}
*/
static extractModifiers(modifiersModels)
{
if(!sc.isArray(modifiersModels) || 0 === modifiersModels.length){
return [];
}
let levelModifiers = [];
for(let modifierData of modifiersModels){
let modifier = new Modifier(modifierData);
levelModifiers.push(modifier);
}
return levelModifiers;
}
}
module.exports.LevelsGenerator = LevelsGenerator;
================================================
FILE: lib/actions/server/storage/modifiers-generator.js
================================================
/**
*
* Reldens - Skills - ModifiersGenerator
*
* Generates modifier instances from database models.
*
*/
const { Modifier } = require('@reldens/modifiers');
const { sc } = require('@reldens/utils');
class ModifiersGenerator
{
/**
* @param {Array} modifiersModels
* @returns {Array}
*/
static fromModifiersModels(modifiersModels)
{
if(!sc.isArray(modifiersModels) || 0 === modifiersModels.length){
return [];
}
let modifiers = [];
for(let modifierModel of modifiersModels){
let modifier = new Modifier(modifierModel);
modifiers.push(modifier);
}
return modifiers;
}
}
module.exports.ModifiersGenerator = ModifiersGenerator;
================================================
FILE: lib/actions/server/storage/skills-generator.js
================================================
/**
*
* Reldens - Skills - SkillsGenerator
*
* Generates skill data structures and instances from database models.
*
*/
const { ModifiersGenerator } = require('./modifiers-generator');
const { ConditionsGenerator } = require('./conditions-generator');
const { ErrorManager, Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
*/
class SkillsGenerator
{
/**
* @param {Array} skillsModels
* @param {Object} skillsClasses
* @param {EventsManager} events
* @returns {Object}
*/
static dataFromSkillsModelsWithClasses(skillsModels, skillsClasses, events)
{
if(0 === skillsModels.length){
return {};
}
let skillsList = {};
for(let skillModel of skillsModels){
let skillClass = sc.hasOwn(skillsClasses, skillModel.type) ? skillsClasses[skillModel.type] : false;
if(!skillClass){
ErrorManager.error('Undefined skill type in skillsList:' + skillModel.type);
}
// force to use the same events manager instance used on the main package:
skillModel.events = events;
this.enrichWithAttackData(skillModel);
this.enrichWithPhysicalData(skillModel);
skillsList[skillModel.key] = {class: skillClass, data: skillModel};
}
return {skillsModels, skillsList};
}
/**
* @param {Array} levelSkillsModels
* @param {Object} owner
* @param {string} ownerIdProperty
* @param {Object} skillsClassesList
* @param {EventsManager} events
* @returns {Object}
*/
static skillsByLevelsFromSkillsModels(levelSkillsModels, owner, ownerIdProperty, skillsClassesList, events)
{
if(!sc.isArray(levelSkillsModels) || 0 === levelSkillsModels.length){
return {};
}
let skillsByLevel = {};
for(let skillData of levelSkillsModels){
//Logger.debug('Skill data:', skillData);
let skillLevel = skillData['related_skills_levels'];
if (!skillLevel){
Logger.debug('Skill level is not defined.', skillLevel);
continue;
}
let levelKey = Number(skillLevel.key);
if(!sc.hasOwn(skillsByLevel, levelKey)){
skillsByLevel[levelKey] = {};
}
let skillModel = skillData.related_skills_skill;
skillModel.owner = owner;
skillModel.ownerIdProperty = ownerIdProperty;
skillModel.events = events;
this.enrichWithAttackData(skillModel);
this.enrichWithPhysicalData(skillModel);
skillModel.ownerConditions = ConditionsGenerator.fromConditionsModels(
skillModel['related_skills_skill_owner_conditions']
);
// @NOTE: skill effects are modifiers that will affect the skill owner or target.
skillModel.ownerEffects = ModifiersGenerator.fromModifiersModels(
skillModel['related_skills_skill_owner_effects']
);
skillModel.targetEffects = ModifiersGenerator.fromModifiersModels(
skillModel['related_skills_skill_target_effects']
);
skillsByLevel[levelKey][skillModel.key] = new skillsClassesList[skillModel.key]['class'](skillModel);
}
return skillsByLevel;
}
/**
* @param {Object} skillModel
*/
static enrichWithPhysicalData(skillModel)
{
if(!sc.isTrue(skillModel, 'related_skills_skill_physical_data')){
return;
}
// @TODO - BETA - Make physical properties configurable.
let physicalProps = [
'magnitude',
'objectWidth',
'objectHeight',
'validateTargetOnHit'
];
for(let i of physicalProps){
skillModel[i] = skillModel['related_skills_skill_physical_data'][i];
}
}
/**
* @param {Object} skillModel
*/
static enrichWithAttackData(skillModel)
{
if(!sc.isTrue(skillModel, 'related_skills_skill_attack')){
return;
}
skillModel['attackProperties'] = skillModel['related_skills_skill_attack'].attackProperties?.split(',') || [];
skillModel['defenseProperties'] = skillModel['related_skills_skill_attack'].defenseProperties?.split(',') || [];
skillModel['aimProperties'] = skillModel['related_skills_skill_attack'].aimProperties?.split(',') || [];
skillModel['dodgeProperties'] = skillModel['related_skills_skill_attack'].dodgeProperties?.split(',') || [];
let attackProps = [
'affectedProperty',
'allowEffectBelowZero',
'hitDamage',
'applyDirectDamage',
'dodgeFullEnabled',
'dodgeOverAimSuccess',
'damageAffected',
'criticalAffected'
];
for(let i of attackProps){
skillModel[i] = skillModel['related_skills_skill_attack'][i];
}
}
}
module.exports.SkillsGenerator = SkillsGenerator;
================================================
FILE: lib/actions/server/storage-observer.js
================================================
/**
*
* Reldens - Skills - StorageObserver
*
* Observes skill events and persists data changes to storage.
*
*/
const { ModelsManager } = require('./models-manager');
const { SkillsEvents } = require('@reldens/skills');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
*
* @typedef {Object} StorageObserverProps
* @property {Object} classPath
* @property {Object} [modelsManagerConfig]
* @property {BaseDataServer} dataServer
* @property {ModelsManager} [modelsManager]
*/
class StorageObserver
{
/**
* @param {StorageObserverProps} props
*/
constructor(props)
{
/** @type {Object} */
this.classPath = props.classPath;
let modelsManagerConfig = sc.get(props, 'modelsManagerConfig', {});
if(this.classPath.events){
modelsManagerConfig['events'] = this.classPath.events;
}
/** @type {BaseDataServer|false} */
this.dataServer = sc.get(props, 'dataServer', false);
modelsManagerConfig['dataServer'] = this.dataServer;
/** @type {ModelsManager} */
this.modelsManager = sc.isTrue(props, 'modelsManager')
? props.modelsManager
: new ModelsManager(modelsManagerConfig);
}
/**
* @returns {boolean|void}
*/
registerListeners()
{
if(!this.classPath){
Logger.error('Class Path data undefined for Storage Observer.');
return false;
}
if(!this.modelsManager){
Logger.error('ModelsManager undefined for Skills Storage Observer.');
return false;
}
let ownerEventKey = this.classPath.getOwnerEventKey();
this.classPath.listenEvent(
SkillsEvents.LEVEL_UP,
this.saveLevelUpData.bind(this),
this.classPath.getOwnerUniqueEventKey('levelUpStorage'),
ownerEventKey
);
this.classPath.listenEvent(
SkillsEvents.LEVEL_EXPERIENCE_ADDED,
this.updateExperience.bind(this),
this.classPath.getOwnerUniqueEventKey('expAddStorage'),
ownerEventKey
);
this.classPath.listenEvent(
SkillsEvents.SKILL_APPLY_OWNER_EFFECTS,
this.saveOwnerData.bind(this),
this.classPath.getOwnerUniqueEventKey('applyOwnerEffectsStorage'),
ownerEventKey
);
this.classPath.listenEvent(
SkillsEvents.SKILL_EFFECT_TARGET_MODIFIERS,
this.saveTargetData.bind(this),
this.classPath.getOwnerUniqueEventKey('applyTargetEffectsStorage'),
ownerEventKey
);
}
/**
* @param {Object} skill
* @returns {Promise}
*/
async saveTargetData(skill)
{
if(!sc.isFunction(skill?.target?.persistData)){
return false;
}
return await skill.target.persistData();
}
/**
* @param {Object} skill
* @returns {Promise}
*/
async saveOwnerData(skill)
{
return await skill.owner.persistData();
}
/**
* @param {Object} levelsSet
* @returns {Promise}
*/
async updateExperience(levelsSet)
{
return await this.modelsManager.updateExperience(levelsSet);
}
/**
* @param {Object} levelsSet
* @returns {Promise}
*/
async saveLevelUpData(levelsSet)
{
await this.modelsManager.updateLevel(levelsSet);
return await levelsSet.owner.persistData();
}
}
module.exports.StorageObserver = StorageObserver;
================================================
FILE: lib/admin/server/entities-config-override.js
================================================
/**
*
* Reldens - EntitiesConfigOverrides
*
* Configuration object that maps entity types to their parent menu categories in the admin panel.
* Each menu object contains a parentItemLabel property that defines the navigation group.
*
*/
/**
* @typedef {Object} MenuConfig
* @property {string} parentItemLabel
*/
/** @type {MenuConfig} */
let skillsMenu = {parentItemLabel: 'Skills'};
/** @type {MenuConfig} */
let classPathMenu = {parentItemLabel: 'Classes & Levels'};
/** @type {MenuConfig} */
let settingsMenu = {parentItemLabel: 'Settings'};
/** @type {MenuConfig} */
let usersMenu = {parentItemLabel: 'Users'};
/** @type {MenuConfig} */
let adsMenu = {parentItemLabel: 'Ads'};
/** @type {MenuConfig} */
let audioMenu = {parentItemLabel: 'Audio'};
/** @type {MenuConfig} */
let chatMenu = {parentItemLabel: 'Chat'};
/** @type {MenuConfig} */
let featuresMenu = {parentItemLabel: 'Features'};
/** @type {MenuConfig} */
let itemsMenu = {parentItemLabel: 'Items & Inventory'};
/** @type {MenuConfig} */
let objectsMenu = {parentItemLabel: 'Game Objects'};
/** @type {MenuConfig} */
let respawnMenu = {parentItemLabel: 'Respawn'};
/** @type {MenuConfig} */
let rewardsMenu = {parentItemLabel: 'Rewards'};
/** @type {MenuConfig} */
let roomsMenu = {parentItemLabel: 'Rooms'};
/** @type {MenuConfig} */
let snippetsMenu = {parentItemLabel: 'Translations'};
/** @type {MenuConfig} */
let clanMenu = {parentItemLabel: 'Clan'};
module.exports.EntitiesConfigOverrides = {
adsBanner: adsMenu,
ads: adsMenu,
adsEventVideo: adsMenu,
adsPlayed: adsMenu,
adsProviders: adsMenu,
adsTypes: adsMenu,
audioCategories: audioMenu,
audio: audioMenu,
audioMarkers: audioMenu,
audioPlayerConfig: audioMenu,
chat: chatMenu,
chatMessageTypes: chatMenu,
clan: clanMenu,
clanLevels: clanMenu,
clanLevelsModifiers: clanMenu,
clanMembers: clanMenu,
config: settingsMenu,
configTypes: settingsMenu,
dropsAnimations: rewardsMenu,
features: featuresMenu,
itemsGroup: itemsMenu,
itemsInventory: itemsMenu,
itemsItem: itemsMenu,
itemsItemModifiers: itemsMenu,
itemsTypes: itemsMenu,
locale: snippetsMenu,
objectsAnimations: objectsMenu,
objectsAssets: objectsMenu,
objects: objectsMenu,
objectsItemsInventory: objectsMenu,
objectsItemsRequirements: objectsMenu,
objectsItemsRewards: objectsMenu,
objectsSkills: objectsMenu,
objectsStats: objectsMenu,
objectsTypes: objectsMenu,
operationTypes: settingsMenu,
players: usersMenu,
playersState: usersMenu,
playersStats: usersMenu,
respawn: respawnMenu,
rewards: rewardsMenu,
rewardsEvents: rewardsMenu,
rewardsEventsState: rewardsMenu,
rewardsModifiers: rewardsMenu,
roomsChangePoints: roomsMenu,
rooms: roomsMenu,
roomsReturnPoints: roomsMenu,
scoresDetail: usersMenu,
scores: usersMenu,
skillsClassLevelUpAnimations: classPathMenu,
skillsClassPath: classPathMenu,
skillsClassPathLevelLabels: classPathMenu,
skillsClassPathLevelSkills: classPathMenu,
skillsGroups: skillsMenu,
skillsLevels: classPathMenu,
skillsLevelsModifiersConditions: skillsMenu,
skillsLevelsModifiers: classPathMenu,
skillsLevelsSet: classPathMenu,
skillsOwnersClassPath: usersMenu,
skillsSkillAnimations: skillsMenu,
skillsSkillAttack: skillsMenu,
skillsSkill: skillsMenu,
skillsSkillGroupRelation: skillsMenu,
skillsSkillOwnerConditions: skillsMenu,
skillsSkillOwnerEffectsConditions: skillsMenu,
skillsSkillOwnerEffects: skillsMenu,
skillsSkillPhysicalData: skillsMenu,
skillsSkillTargetEffectsConditions: skillsMenu,
skillsSkillTargetEffects: skillsMenu,
skillsSkillType: skillsMenu,
snippets: snippetsMenu,
stats: usersMenu,
targetOptions: objectsMenu,
users: usersMenu,
usersLocale: usersMenu,
usersLogin: usersMenu
};
================================================
FILE: lib/admin/server/plugin.js
================================================
/**
*
* Reldens - AdminPlugin
*
* Plugin that sets up the administration panel and related management features including
* server shutdown, theme manager, maps wizard, objects importer, skills importer, and rooms management.
*
*/
const { PluginInterface } = require('../../features/plugin-interface');
const { SetupServerProperties } = require('../../features/server/setup-server-properties');
const { CreateAdminSubscriber } = require('./subscribers/create-admin-subscriber');
const { MapsWizardSubscriber } = require('./subscribers/maps-wizard-subscriber');
const { ObjectsImporterSubscriber } = require('./subscribers/objects-importer-subscriber');
const { SkillsImporterSubscriber } = require('./subscribers/skills-importer-subscriber');
const { ShutdownSubscriber } = require('./subscribers/shutdown-subscriber');
const { ThemeManagerSubscriber } = require('./subscribers/theme-manager-subscriber');
const { RoomsEntitySubscriber } = require('./subscribers/rooms-entity-subscriber');
const { GeneratorsRoutesSubscriber } = require('./subscribers/generators-routes-subscriber');
const { FileHandler } = require('@reldens/server-utils');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('../../config/server/manager').ConfigManager} ConfigManager
*/
class AdminPlugin extends PluginInterface
{
/**
* @param {SetupServerProperties} setupServerProperties
* @return {Promise}
*/
async setup(setupServerProperties)
{
if(!(setupServerProperties instanceof SetupServerProperties)){
Logger.error('The setupServerProperties param must be an instance of SetupServerProperties.');
return false;
}
if(!setupServerProperties.validate()){
return false;
}
setupServerProperties.assignProperties(this);
/** @type {CreateAdminSubscriber} */
this.createAdminSubscriber = new CreateAdminSubscriber();
/** @type {Object} */
this.subscribers = {};
this.listenEvents();
return true;
}
listenEvents()
{
if(!this.events){
return;
}
this.events.on('reldens.serverBeforeListen', async (event) => {
await this.createAdminSubscriber.activateAdmin(event);
});
this.events.on('reldens.beforeCreateAdminManager', async (event) => {
if(!event.serverManager?.dataServerConfig?.translations){
Logger.debug('Translations not available on beforeCreateAdminManage event.');
return;
}
sc.deepMergeProperties(event.serverManager.dataServerConfig.translations, {
messages: {
loginWelcome: 'Administration Panel - Login',
reldensTitle: 'Reldens - Administration Panel'
},
labels: {
navigation: 'Reldens - Administration Panel',
loginWelcome: 'Reldens',
pages: 'Server Management',
management: 'Management',
themeManager: 'Theme Manager',
mapsWizard: 'Maps Generation and Import',
objectsImport: 'Objects Import',
skillsImport: 'Skills Import',
shuttingDown: 'Server is shutting down in:',
submitShutdownLabel: 'Shutdown Server',
submitCancelLabel: 'Cancel Server Shutdown'
}
});
this.extendAdminTemplates(event);
});
this.events.on('reldens.beforeSetupAdminManager', async (event) => {
let adminManager = event.serverManager.serverAdmin;
if(!adminManager){
Logger.error('The admin manager does not exist to setup the AdminPlugin on beforeSetupAdminManager.');
return false;
}
let themeManager = event.serverManager.themeManager;
this.subscribers.shutdown = new ShutdownSubscriber(
adminManager,
this.config,
event.serverManager.serverBroadcast.bind(event.serverManager)
);
this.subscribers.themeManager = new ThemeManagerSubscriber(
adminManager,
this.config,
themeManager
);
this.subscribers.mapsWizard = new MapsWizardSubscriber(adminManager, this.config, themeManager);
this.subscribers.objectsImporter = new ObjectsImporterSubscriber(adminManager, themeManager);
this.subscribers.skillsImporter = new SkillsImporterSubscriber(adminManager, themeManager);
this.subscribers.roomsEntity = new RoomsEntitySubscriber(adminManager, this.config);
this.subscribers.generatorsRoutes = new GeneratorsRoutesSubscriber(
adminManager,
themeManager.projectGenerateDataPath,
themeManager.projectGeneratedDataPath
);
});
}
extendAdminTemplates(event)
{
if(!event?.serverManager?.themeManager?.adminTemplatesList?.fields?.edit){
return;
}
let themeManager = event.serverManager.themeManager;
themeManager.adminTemplatesList.fields.edit['tileset-file-item'] = 'tileset-file-item.html';
themeManager.adminTemplatesList.fields.edit['tileset-alert-wrapper'] = 'tileset-alert-wrapper.html';
let templatesPath = FileHandler.joinPaths(themeManager.projectAdminTemplatesPath, 'fields', 'edit');
themeManager.adminTemplates.fields.edit['tileset-file-item'] = FileHandler.joinPaths(
templatesPath,
'tileset-file-item.html'
);
themeManager.adminTemplates.fields.edit['tileset-alert-wrapper'] = FileHandler.joinPaths(
templatesPath,
'tileset-alert-wrapper.html'
);
}
}
module.exports.AdminPlugin = AdminPlugin;
================================================
FILE: lib/admin/server/room-map-tilesets-validator.js
================================================
/**
*
* Reldens - RoomMapTilesetsValidator
*
* Validates room map tilesets and optionally overrides scene_images with map file as source of truth.
*
*/
const { Logger, sc } = require('@reldens/utils');
const { FileHandler } = require('@reldens/server-utils');
/**
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
* @typedef {import('../../game/server/config-manager').ConfigManager} ConfigManager
*/
class RoomMapTilesetsValidator
{
/**
* @param {BaseDataServer} dataServer
* @param {ConfigManager} config
*/
constructor(dataServer, config)
{
this.dataServer = dataServer;
this.config = config;
this.roomsRepository = dataServer.getEntity('rooms');
}
/**
* @param {Object} event
* @returns {Promise}
*/
async validate(event)
{
let overrideEnabled = this.config.getWithoutLogs('server/rooms/maps/overrideSceneImagesWithMapFile', true);
if(!overrideEnabled){
return false;
}
let driverResource = sc.get(event, 'driverResource', false);
if(!driverResource || 'rooms' !== driverResource.entityKey){
return false;
}
let entityData = sc.get(event, 'entityData', false);
if(!entityData){
return false;
}
let mapFilename = sc.get(entityData, 'map_filename', '');
if(!mapFilename){
return false;
}
let options = sc.get(driverResource, 'options', {});
let uploadProperties = sc.get(options, 'properties', {});
let mapProperty = sc.get(uploadProperties, 'map_filename', false);
let sceneImagesProperty = sc.get(uploadProperties, 'scene_images', false);
if(!mapProperty || !sceneImagesProperty){
return false;
}
let mapBucket = sc.get(mapProperty, 'bucket', '');
let sceneImagesBucket = sc.get(sceneImagesProperty, 'bucket', '');
if(!mapBucket || !sceneImagesBucket){
return false;
}
let mapData = this.readMapFile(mapBucket, mapFilename, entityData.id);
if(!mapData){
return false;
}
let tilesetImages = this.extractTilesetImages(mapData);
if(0 === tilesetImages.length){
return false;
}
let currentSceneImages = sc.get(entityData, 'scene_images', '');
let currentSceneImagesArray = currentSceneImages ? currentSceneImages.split(',') : [];
if(this.arraysAreEqual(tilesetImages, currentSceneImagesArray)){
return false;
}
let allImagesExist = this.validateImagesExist(tilesetImages, sceneImagesBucket, entityData.id, mapFilename);
if(!allImagesExist){
Logger.error('Cannot override scene_images: some tileset images do not exist.', {
roomId: entityData.id,
mapFilename,
tilesetImages
});
return false;
}
return await this.overrideSceneImages(entityData.id, tilesetImages);
}
/**
* @param {string} mapBucket
* @param {string} mapFilename
* @param {number} roomId
* @returns {Object|boolean}
*/
readMapFile(mapBucket, mapFilename, roomId)
{
let mapFilePath = FileHandler.joinPaths(mapBucket, mapFilename);
if(!FileHandler.exists(mapFilePath)){
Logger.warning('Map file not found after room save.', {mapFilePath, roomId});
return false;
}
let mapContents = FileHandler.readFile(mapFilePath);
if(!mapContents){
Logger.warning('Could not read map file contents.', {mapFilePath, roomId});
return false;
}
try {
return JSON.parse(mapContents);
} catch (error) {
Logger.warning('Invalid JSON in map file.', {mapFilePath, roomId, error: error.message});
return false;
}
}
/**
* @param {Object} mapData
* @returns {Array}
*/
extractTilesetImages(mapData)
{
let tilesets = sc.get(mapData, 'tilesets', []);
if(!sc.isArray(tilesets) || 0 === tilesets.length){
return [];
}
let images = [];
for(let tileset of tilesets){
let tilesetImage = sc.get(tileset, 'image', '');
if(!tilesetImage){
continue;
}
let imageFileName = tilesetImage.split('/').pop();
if(-1 === images.indexOf(imageFileName)){
images.push(imageFileName);
}
}
return images;
}
/**
* @param {Array} tilesetImages
* @param {string} sceneImagesBucket
* @param {number} roomId
* @param {string} mapFilename
* @returns {boolean}
*/
validateImagesExist(tilesetImages, sceneImagesBucket, roomId, mapFilename)
{
for(let imageFileName of tilesetImages){
let imageFilePath = FileHandler.joinPaths(sceneImagesBucket, imageFileName);
if(!FileHandler.exists(imageFilePath)){
Logger.warning('Tileset image not found in scene_images folder.', {
imageFileName,
imageFilePath,
roomId,
mapFilename
});
return false;
}
}
return true;
}
/**
* @param {Array} array1
* @param {Array} array2
* @returns {boolean}
*/
arraysAreEqual(array1, array2)
{
if(array1.length !== array2.length){
return false;
}
let sorted1 = [...array1].sort();
let sorted2 = [...array2].sort();
for(let i = 0; i < sorted1.length; i++){
if(sorted1[i] !== sorted2[i]){
return false;
}
}
return true;
}
/**
* @param {number} roomId
* @param {Array} tilesetImages
* @returns {Promise}
*/
async overrideSceneImages(roomId, tilesetImages)
{
let sceneImagesValue = tilesetImages.join(',');
let updateResult = await this.roomsRepository.updateById(roomId, {scene_images: sceneImagesValue});
if(!updateResult){
Logger.error('Failed to override scene_images with map file tilesets.', {roomId, tilesetImages});
return false;
}
Logger.info('Overrode scene_images with map file tilesets.', {roomId, tilesetImages, sceneImagesValue});
return true;
}
/**
* @param {Object} entityData - The loaded entity data
* @param {Object} driverResource - Driver resource with properties config
* @returns {Array|boolean}
*/
extractTilesetImagesFromEntity(entityData, driverResource)
{
if(!entityData){
return false;
}
let mapFilename = sc.get(entityData, 'map_filename', '');
if(!mapFilename){
return false;
}
let mapBucket = sc.get(driverResource?.options?.properties?.map_filename, 'bucket', '');
if(!mapBucket){
return false;
}
let mapData = this.readMapFile(mapBucket, mapFilename, entityData.id);
if(!mapData){
return false;
}
return this.extractTilesetImages(mapData);
}
}
module.exports.RoomMapTilesetsValidator = RoomMapTilesetsValidator;
================================================
FILE: lib/admin/server/rooms-file-upload-renderer.js
================================================
/**
*
* Reldens - RoomsFileUploadRenderer
*
*/
const { sc } = require('@reldens/utils');
class RoomsFileUploadRenderer
{
async renderFileUploadField(eventData)
{
let propertyKey = eventData.propertyKey;
if('scene_images' !== propertyKey){
return;
}
let protectedFiles = sc.get(eventData.renderedEditProperties, 'tilesetImages', []);
if(0 === protectedFiles.length){
return;
}
if(!sc.get(eventData.renderedEditProperties, 'overrideSceneImagesEnabled', false)){
return;
}
let editTemplates = eventData?.adminFilesContents?.fields?.edit;
let fileItemTemplate = sc.get(editTemplates, 'tileset-file-item', false);
if(!fileItemTemplate){
return;
}
let alertWrapperTemplate = sc.get(editTemplates, 'tileset-alert-wrapper', false);
if(!alertWrapperTemplate){
return;
}
let filesArray = sc.get(eventData.templateData, 'filesArray', []);
let renderedFileItems = [];
for(let file of filesArray){
let filename = file.filename || file;
renderedFileItems.push(await eventData.adminContentsRender(fileItemTemplate, {
fieldName: propertyKey,
filename,
isProtected: -1 !== protectedFiles.indexOf(filename)
}));
}
eventData.templateData.renderedFiles = await eventData.adminContentsRender(
alertWrapperTemplate,
{renderedFileItems: renderedFileItems.join('')}
);
eventData.templateData.renderedAlert = '';
}
}
module.exports.RoomsFileUploadRenderer = RoomsFileUploadRenderer;
================================================
FILE: lib/admin/server/subscribers/create-admin-subscriber.js
================================================
/**
*
* Reldens - CreateAdminSubscriber
*
* Subscriber that handles the creation and initialization of the admin panel.
* Configures entities, templates, authentication, and branding for the administration interface.
*
*/
const { AdminManager } = require('@reldens/cms/lib/admin-manager');
const { AdminManagerValidator } = require('@reldens/cms/lib/admin-manager-validator');
const { AdminEntitiesGenerator } = require('@reldens/cms/lib/admin-entities-generator');
const { AdminTemplatesLoader } = require('@reldens/cms/lib/admin-templates-loader');
const { AdminDistHelper } = require('@reldens/cms/lib/admin-dist-helper');
const { DefaultTranslations } = require('@reldens/cms/lib/admin-manager/default-translations');
const { MimeTypes } = require('../../../game/mime-types');
const { GameConst } = require('../../../game/constants');
const { FileHandler } = require('@reldens/server-utils');
const { Logger, EnvVar, sc } = require('@reldens/utils');
/**
* @typedef {import('../../../game/server/manager').ServerManager} ServerManager
* @typedef {import('../../../game/server/theme-manager').ThemeManager} ThemeManager
* @typedef {import('../../../config/server/manager').ConfigManager} ConfigManager
*/
class CreateAdminSubscriber
{
/**
* @param {Object} event
* @returns {Promise}
*/
async activateAdmin(event)
{
let serverManager = sc.get(event, 'serverManager', false);
if(!this.validate(serverManager)){
return false;
}
let entitiesGenerator = new AdminEntitiesGenerator();
serverManager.events.emit('reldens.beforeCreateAdminManager', {serverManager});
let dataServerConfig = serverManager.dataServerConfig;
let dataServer = serverManager.dataServer;
let themeManager = serverManager.themeManager;
let adminConfig = {
events: serverManager.events,
dataServer,
authenticationCallback: serverManager.loginManager.roleAuthenticationCallback.bind(
serverManager.loginManager
),
app: serverManager.app,
appServerFactory: serverManager.appServerFactory,
entities: entitiesGenerator.generate(dataServerConfig.loadedEntities, dataServer.entityManager.entities),
validator: new AdminManagerValidator(),
autoSyncDistCallback: AdminDistHelper.copyBucketFilesToDist,
updateAdminAssetsDistOnActivation: themeManager.copyAdminAssetsToDist.bind(themeManager),
renderCallback: themeManager.templateEngine.render.bind(themeManager.templateEngine),
secret: EnvVar.nonEmptyString(process.env, 'RELDENS_ADMIN_SECRET', ''),
rootPath: EnvVar.nonEmptyString(process.env, 'RELDENS_ADMIN_ROUTE_PATH', '/reldens-admin'),
translations: Object.assign({}, DefaultTranslations, dataServerConfig?.translations || {}),
adminFilesContents: await AdminTemplatesLoader.fetchAdminFilesContents(themeManager.adminTemplates),
mimeTypes: MimeTypes,
allowedExtensions: {
audio: ['.aac', '.mid', '.midi', '.mp3', '.ogg', '.oga', '.opus', '.wav', '.weba', '.3g2'],
image: ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp'],
text: ['.json', '.jsonld', '.txt']
},
...sc.deepMergeProperties(
this.fetchConfigurations(serverManager.configManager),
await this.fetchFilesContents(themeManager)
)
};
serverManager.serverAdmin = new AdminManager(adminConfig);
serverManager.events.emit('reldens.beforeSetupAdminManager', {serverManager});
await serverManager.serverAdmin.setupAdmin();
serverManager.events.emit('reldens.afterCreateAdminManager', {serverManager});
}
/**
* @param {ServerManager|false} serverManager
* @returns {boolean}
*/
validate(serverManager)
{
if(!serverManager){
Logger.error('ServerManager not found in CreateAdminSubscriber.');
return false;
}
if(!serverManager.events){
Logger.error('EventsManager not found in CreateAdminSubscriber.');
return false;
}
if(!serverManager.themeManager){
Logger.error('ThemeManager not found in CreateAdminSubscriber.');
return false;
}
if(!serverManager.configManager){
Logger.error('ConfigManager not found in CreateAdminSubscriber.');
return false;
}
return true;
}
/**
* @param {ConfigManager} config
* @returns {Object}
*/
fetchConfigurations(config)
{
let path = 'server/admin/';
return {
adminRoleId: config.get(path+'roleId', 1),
stylesFilePath: config.getWithoutLogs(path+'stylesPath', '/css/'+GameConst.STRUCTURE.ADMIN_CSS_FILE),
scriptsFilePath: config.getWithoutLogs(path+'scriptsPath', '/'+GameConst.STRUCTURE.ADMIN_JS_FILE),
branding: {
companyName: config.getWithoutLogs(path+'companyName', 'Reldens - Administration Panel'),
logo: config.getWithoutLogs(path+'logoPath', '/assets/web/reldens-your-logo-mage.png'),
favicon: config.getWithoutLogs(path+'faviconPath', '/assets/web/favicon.ico'),
copyRight: config.getWithoutLogs(path+'copyRight', '')
}
};
}
/**
* @param {ThemeManager} themeManager
* @returns {Promise}
*/
async fetchFilesContents(themeManager)
{
return {
branding: {
copyRight: await FileHandler.fetchFileContents(
FileHandler.joinPaths(
themeManager.projectAdminTemplatesPath,
themeManager.adminTemplatesList.defaultCopyRight
)
)
}
}
}
}
module.exports.CreateAdminSubscriber = CreateAdminSubscriber;
================================================
FILE: lib/admin/server/subscribers/generators-routes-subscriber.js
================================================
/**
*
* Reldens - GeneratorsRoutesSubscriber
*
* Subscriber that sets up static file routes in the admin panel for generated and source data.
* Provides authenticated access to the generate-data and generated directories.
*
*/
const { Logger } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@reldens/cms/lib/admin-manager').AdminManager} AdminManager
*/
class GeneratorsRoutesSubscriber
{
/**
* @param {AdminManager} adminManager
* @param {string} projectGenerateDataPath
* @param {string} projectGeneratedDataPath
*/
constructor(adminManager, projectGenerateDataPath, projectGeneratedDataPath)
{
/** @type {EventsManager} */
this.events = adminManager.events;
/** @type {Function} */
this.isAuthenticated = adminManager.router.isAuthenticated.bind(adminManager.router);
/** @type {string} */
this.projectGenerateDataPath = projectGenerateDataPath;
/** @type {string} */
this.projectGeneratedDataPath = projectGeneratedDataPath;
this.listenEvents();
}
/**
* @returns {boolean|void}
*/
listenEvents()
{
if(!this.events){
Logger.error('EventsManager not found on GeneratorsRoutesSubscriber.');
return false;
}
this.events.on('reldens.setupAdminManagers', async (event) => {
this.setupRoutes(event.adminManager);
});
}
/**
* @param {AdminManager} adminManager
* @returns {boolean|void}
*/
setupRoutes(adminManager)
{
if(!adminManager.router.adminRouter){
Logger.error('AdminRouter is not available in GeneratorsRoutesSubscriber.setupRoutes.');
return false;
}
adminManager.router.adminRouter.use(
'/generate-data',
this.isAuthenticated.bind(this),
adminManager.applicationFramework.static(this.projectGenerateDataPath)
);
adminManager.router.adminRouter.use(
'/generated',
this.isAuthenticated.bind(this),
adminManager.applicationFramework.static(this.projectGeneratedDataPath)
);
Logger.info(
'Included administration panel static routes.',
this.projectGenerateDataPath,
this.projectGeneratedDataPath
);
}
}
module.exports.GeneratorsRoutesSubscriber = GeneratorsRoutesSubscriber;
================================================
FILE: lib/admin/server/subscribers/maps-wizard-subscriber.js
================================================
/**
*
* Reldens - MapsWizardSubscriber
*
* Subscriber that handles map generation and import functionality in the admin panel.
* Supports multiple map generation strategies and batch import with associations.
*
*/
const { MapsImporter } = require('../../../import/server/maps-importer');
const { AllowedFileTypes } = require('../../../game/allowed-file-types');
const {
RandomMapGenerator,
LayerElementsObjectLoader,
LayerElementsCompositeLoader,
MultipleByLoaderGenerator,
MultipleWithAssociationsByLoaderGenerator
} = require('@reldens/tile-map-generator');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@reldens/cms/lib/admin-manager').AdminManager} AdminManager
* @typedef {import('../../../config/server/manager').ConfigManager} ConfigManager
* @typedef {import('../../../game/server/theme-manager').ThemeManager} ThemeManager
* @typedef {import('express').Request} ExpressRequest
* @typedef {import('express').Response} ExpressResponse
*/
class MapsWizardSubscriber
{
/**
* @param {AdminManager} adminManager
* @param {ConfigManager} configManager
* @param {ThemeManager} themeManager
*/
constructor(adminManager, configManager, themeManager)
{
/** @type {string} */
this.mapsWizardPath = '/maps-wizard';
/** @type {string} */
this.rootPath = '';
/** @type {ThemeManager} */
this.themeManager = themeManager;
/** @type {EventsManager} */
this.events = adminManager.events;
/** @type {MapsImporter} */
this.mapsImporter = new MapsImporter({configManager, dataServer: adminManager.dataServer, themeManager});
/** @type {Array} */
this.fields = [{name: 'generatorImages'}, {name: 'generatorJsonFiles'}];
/** @type {Object} */
this.buckets = {
generatorImages: this.themeManager.projectGenerateDataPath,
generatorJsonFiles: this.themeManager.projectGenerateDataPath
};
/** @type {Object} */
this.allowedFileTypes = {
generatorImages: AllowedFileTypes.IMAGE,
generatorJsonFiles: AllowedFileTypes.TEXT
};
/** @type {Function} */
this.uploader = adminManager.uploaderFactory.createUploader(this.fields, this.buckets, this.allowedFileTypes);
/** @type {Function} */
this.render = adminManager.contentsBuilder.render.bind(adminManager.contentsBuilder);
/** @type {Function} */
this.renderRoute = adminManager.contentsBuilder.renderRoute.bind(adminManager.contentsBuilder);
/** @type {Function} */
this.isAuthenticated = adminManager.router.isAuthenticated.bind(adminManager.router);
/** @type {Object} */
this.mapsWizardHandlers = {
'elements-object-loader': LayerElementsObjectLoader,
'elements-composite-loader': LayerElementsCompositeLoader,
'multiple-by-loader': MultipleByLoaderGenerator,
'multiple-with-association-by-loader': MultipleWithAssociationsByLoaderGenerator
};
this.listenEvents();
}
/**
* @returns {boolean|void}
*/
listenEvents()
{
if(!this.events){
Logger.error('EventsManager not found on MapsWizardSubscriber.');
return false;
}
this.events.on('reldens.setupAdminManagers', async (event) => {
this.setupRoutes(event.adminManager);
});
this.events.on('reldens.eventBuildSideBarBefore', async (event) => {
if(!event.navigationContents['Wizards']){
event.navigationContents['Wizards'] = {};
}
let translatedLabel = event.adminManager.translations.labels['mapsWizard'];
event.navigationContents['Wizards'][translatedLabel] = await event.adminManager.contentsBuilder.render(
event.adminManager.adminFilesContents.sideBarItem,
{name: translatedLabel, path: event.adminManager.rootPath+this.mapsWizardPath}
);
});
this.events.on('reldens.buildAdminContentsAfter', async (event) => {
let pageContent = await this.render(
event.adminManager.adminFilesContents.mapsWizard,
{actionPath: event.adminManager.rootPath+this.mapsWizardPath}
);
event.adminManager.contentsBuilder.adminContents.mapsWizard = await this.renderRoute(
pageContent,
event.adminManager.contentsBuilder.adminContents.sideBar
);
});
}
/**
* @param {AdminManager} adminManager
*/
setupRoutes(adminManager)
{
if('' === this.rootPath){
this.rootPath = adminManager.rootPath;
}
adminManager.router.adminRouter.get(this.mapsWizardPath, this.isAuthenticated, async (req, res) => {
return res.send(await this.render(adminManager.contentsBuilder.adminContents.mapsWizard));
});
adminManager.router.adminRouter.post(
this.mapsWizardPath,
this.isAuthenticated,
this.uploader,
async (req, res) => {
if('generate' === req?.body?.mainAction){
return await this.generateMaps(req, res, adminManager);
}
if('import' === req?.body?.mainAction){
return res.redirect(await this.importSelectedMaps(req));
}
}
);
}
/**
* Processes map generation request using the selected handler strategy.
* Supports multiple generation methods, including element loaders and multimap generation.
* @param {ExpressRequest} req
* @param {ExpressResponse} res
* @param {AdminManager} adminManager
* @returns {Promise}
*/
async generateMaps(req, res, adminManager)
{
let selectedHandler = req?.body?.mapsWizardAction;
if(!selectedHandler){
return this.mapsWizardRedirect(res, 'mapsWizardMissingActionError');
}
let generatorData = req?.body?.generatorData;
if(!generatorData){
return this.mapsWizardRedirect(res, 'mapsWizardMissingDataError');
}
let mapData = sc.toJson(generatorData);
if(!mapData){
return this.mapsWizardRedirect(res, 'mapsWizardWrongJsonDataError');
}
let handler = this.mapsWizardHandlers[selectedHandler];
if(!handler){
return this.mapsWizardRedirect(res, 'mapsWizardMissingHandlerError');
}
let mainGenerator = false;
let generatorWithData = false;
let generatedMap = false;
let handlerParams = {mapData, rootFolder: this.themeManager.projectGenerateDataPath};
try {
if('elements-object-loader' === selectedHandler){
mainGenerator = new handler(handlerParams);
await mainGenerator.load();
let generator = new RandomMapGenerator(mainGenerator.mapData);
generatedMap = await generator.generate();
generatorWithData = generator;
}
if('elements-composite-loader' === selectedHandler){
mainGenerator = new handler(handlerParams);
await mainGenerator.load();
let generator = new RandomMapGenerator();
await generator.fromElementsProvider(mainGenerator.mapData);
generatedMap = await generator.generate();
generatorWithData = generator;
}
if('multiple-by-loader' === selectedHandler){
mainGenerator = new MultipleByLoaderGenerator({loaderData: handlerParams});
await mainGenerator.generate();
generatorWithData = mainGenerator;
}
if('multiple-with-association-by-loader' === selectedHandler){
mainGenerator = new MultipleWithAssociationsByLoaderGenerator({loaderData: handlerParams});
await mainGenerator.generate();
generatorWithData = mainGenerator;
}
} catch (error) {
Logger.error('Maps generator error.', selectedHandler, generatorData, error);
return this.mapsWizardRedirect(res, 'mapsWizardGeneratorError');
}
if(!generatorWithData){
Logger.error('Maps not generated, incompatible selected handler.', selectedHandler, generatorData);
return this.mapsWizardRedirect(res, 'mapsWizardSelectedHandlerError');
}
let mapsData = {
maps: [],
actionPath: this.rootPath+this.mapsWizardPath,
generatedMapsHandler: selectedHandler,
importAssociationsForChangePoints: Number(mapData.importAssociationsForChangePoints || 0),
importAssociationsRecursively: Number(mapData.importAssociationsRecursively || 0),
verifyTilesetImage: Number(mapData.verifyTilesetImage || 1),
automaticallyExtrudeMaps: Number(mapData.automaticallyExtrudeMaps || 1),
handlerParams: generatorData
};
if(generatedMap){
let tileWidth = generatedMap.tilewidth;
let tileHeight = generatedMap.tileheight;
let mapFileName = generatorWithData.mapFileName;
if(-1 === mapFileName.indexOf('json')){
mapFileName = mapFileName+'.json';
}
mapsData.maps.push({
key: generatorWithData.mapName,
mapWidth: generatedMap.width * tileWidth,
mapHeight: generatedMap.height * tileHeight,
tileWidth,
tileHeight,
mapImage: this.rootPath+'/generated/'+generatorWithData.tileSheetName,
mapJson: this.rootPath+'/generated/'+mapFileName
});
}
if(generatorWithData.generators && generatorWithData.generatedMaps){
for(let i of Object.keys(generatorWithData.generators)){
let generator = generatorWithData.generators[i];
let generatedMap = generatorWithData.generatedMaps[generator.mapName];
let tileWidth = generatedMap.tilewidth;
let tileHeight = generatedMap.tileheight;
let mapFileName = generator.mapFileName;
if(-1 === mapFileName.indexOf('json')){
mapFileName = mapFileName+'.json';
}
let associatedMap = sc.get(mainGenerator.associatedMaps, i, {});
let subMaps = this.mapSubMapsData(
sc.get(associatedMap, 'generatedSubMaps'),
sc.get(associatedMap, 'generators'),
tileWidth,
tileHeight
);
mapsData.maps.push({
key: generator.mapName,
mapWidth: generatedMap.width * tileWidth,
mapHeight: generatedMap.height * tileHeight,
tileWidth,
tileHeight,
mapImage: this.rootPath+'/generated/'+generator.tileSheetName,
mapJson: this.rootPath+'/generated/'+mapFileName,
hasSubMaps: 0 < subMaps.length,
subMaps
});
}
}
if(0 === mapsData.maps.length){
return this.mapsWizardRedirect(res, 'mapsWizardMapsNotGeneratedError');
}
return this.mapsWizardMapsSelection(res, mapsData, adminManager);
}
/**
* @param {Object} generatedSubMaps
* @param {Object} generators
* @param {number} tileWidth
* @param {number} tileHeight
* @returns {Array}
*/
mapSubMapsData(generatedSubMaps, generators, tileWidth, tileHeight)
{
if(!generatedSubMaps){
return [];
}
let subMapsData = [];
for(let i of Object.keys(generatedSubMaps)) {
let subMapData = generatedSubMaps[i];
let generator = generators[i];
let mapFileName = generator.mapFileName;
if(-1 === mapFileName.indexOf('json')){
mapFileName = mapFileName+'.json';
}
subMapsData.push({
key: generator.mapName,
mapWidth: subMapData.width * tileWidth,
mapHeight: subMapData.height * tileHeight,
tileWidth,
tileHeight,
mapImage: this.rootPath + '/generated/' + generator.tileSheetName,
mapJson: this.rootPath + '/generated/' + mapFileName
});
}
return subMapsData;
}
/**
* @param {ExpressResponse} res
* @param {string} result
* @returns {void}
*/
mapsWizardRedirect(res, result)
{
return res.redirect(this.rootPath + this.mapsWizardPath + '?result='+result);
}
/**
* @param {ExpressResponse} res
* @param {Object} data
* @param {AdminManager} adminManager
* @returns {Promise}
*/
async mapsWizardMapsSelection(res, data, adminManager)
{
let renderedView = await this.render(adminManager.adminFilesContents.mapsWizardMapsSelection, data);
return res.send(await this.renderRoute(renderedView, adminManager.contentsBuilder.adminContents.sideBar));
}
/**
* @param {ExpressRequest} req
* @returns {Promise}
*/
async importSelectedMaps(req)
{
let generatedMapData = this.mapGeneratedMapsDataForImport(req.body);
if(!generatedMapData){
return this.rootPath+this.mapsWizardPath+'?result=mapsWizardImportDataError';
}
let importResult = await this.mapsImporter.import(generatedMapData);
if(!importResult){
let errorCode = this.mapsImporter.errorCode || 'mapsWizardImportError';
return this.rootPath+this.mapsWizardPath+'?result='+errorCode;
}
return this.rootPath+this.mapsWizardPath+'?result=success';
}
/**
* @param {Object} data
* @returns {Object|false}
*/
mapGeneratedMapsDataForImport(data)
{
let selectedMaps = sc.get(data, 'selectedMaps', false);
if(!selectedMaps){
return false;
}
let importAssociations = 'multiple-with-association-by-loader' === data.generatedMapsHandler;
let mappedData = {
importAssociationsForChangePoints: importAssociations,
importAssociationsRecursively: importAssociations,
automaticallyExtrudeMaps: data.automaticallyExtrudeMaps,
verifyTilesetImage: data.verifyTilesetImage,
handlerParams: sc.toJson(data.handlerParams),
relativeGeneratedDataPath: 'generate-data/generated',
maps: {}
};
for(let mapKey of selectedMaps){
mappedData.maps[data['map-title-'+mapKey]] = mapKey;
}
return mappedData;
}
}
module.exports.MapsWizardSubscriber = MapsWizardSubscriber;
================================================
FILE: lib/admin/server/subscribers/objects-importer-subscriber.js
================================================
/**
*
* Reldens - ObjectsImporterSubscriber
*
* Subscriber that handles objects import functionality in the admin panel.
* Allows importing game objects data from JSON files or direct form input.
*
*/
const { ObjectsImporter } = require('../../../import/server/objects-importer');
const { AllowedFileTypes } = require('../../../game/allowed-file-types');
const { FileHandler } = require('@reldens/server-utils');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@reldens/cms/lib/admin-manager').AdminManager} AdminManager
* @typedef {import('../../../game/server/theme-manager').ThemeManager} ThemeManager
* @typedef {import('express').Request} ExpressRequest
*/
class ObjectsImporterSubscriber
{
/**
* @param {AdminManager} adminManager
* @param {ThemeManager} themeManager
*/
constructor(adminManager, themeManager)
{
/** @type {string} */
this.objectsImportPath = '/objects-import';
/** @type {string} */
this.rootPath = '';
/** @type {ThemeManager} */
this.themeManager = themeManager;
/** @type {EventsManager} */
this.events = adminManager.events;
/** @type {Function} */
this.render = adminManager.contentsBuilder.render.bind(adminManager.contentsBuilder);
/** @type {Function} */
this.renderRoute = adminManager.contentsBuilder.renderRoute.bind(adminManager.contentsBuilder);
/** @type {Function} */
this.isAuthenticated = adminManager.router.isAuthenticated.bind(adminManager.router);
/** @type {Array} */
this.fields = [{name: 'generatorJsonFiles'}];
/** @type {Object} */
this.buckets = {generatorJsonFiles: this.themeManager.projectGeneratedDataPath};
/** @type {Object} */
this.allowedFileTypes = {generatorJsonFiles: AllowedFileTypes.TEXT};
/** @type {Function} */
this.uploader = adminManager.uploaderFactory.createUploader(this.fields, this.buckets, this.allowedFileTypes);
/** @type {ObjectsImporter} */
this.objectsImporter = new ObjectsImporter(adminManager);
this.listenEvents();
}
/**
* @returns {boolean|void}
*/
listenEvents()
{
if(!this.events){
Logger.error('EventsManager not found on ObjectsImporterSubscriber.');
return false;
}
this.events.on('reldens.setupAdminManagers', async (event) => {
this.setupRoutes(event.adminManager);
});
this.events.on('reldens.eventBuildSideBarBefore', async (event) => {
if(!event.navigationContents['Wizards']){
event.navigationContents['Wizards'] = {};
}
let translatedLabel = event.adminManager.translations.labels['objectsImport'];
event.navigationContents['Wizards'][translatedLabel] = await event.adminManager.contentsBuilder.render(
event.adminManager.adminFilesContents.sideBarItem,
{name: translatedLabel, path: event.adminManager.rootPath+this.objectsImportPath}
);
});
this.events.on('reldens.buildAdminContentsAfter', async (event) => {
let pageContent = await this.render(
event.adminManager.adminFilesContents.objectsImport,
{actionPath: event.adminManager.rootPath+this.objectsImportPath}
);
event.adminManager.contentsBuilder.adminContents.objectsImport = await this.renderRoute(
pageContent,
event.adminManager.contentsBuilder.adminContents.sideBar
);
});
}
/**
* @param {AdminManager} adminManager
*/
setupRoutes(adminManager)
{
if('' === this.rootPath){
this.rootPath = adminManager.rootPath;
}
adminManager.router.adminRouter.get(this.objectsImportPath, this.isAuthenticated, async (req, res) => {
return res.send(await this.render(adminManager.contentsBuilder.adminContents.objectsImport));
});
adminManager.router.adminRouter.post(
this.objectsImportPath,
this.isAuthenticated,
this.uploader,
async (req, res) => {
return res.redirect(await this.importObjects(req));
}
);
}
/**
* @param {ExpressRequest} req
* @returns {Promise}
*/
async importObjects(req)
{
let generateObjectsData = sc.toJson(req?.body?.generatorData);
if(!generateObjectsData){
let fileName = req.files?.generatorJsonFiles?.shift()?.originalname;
if(!fileName){
return this.rootPath+this.objectsImportPath+'?result=objectsImportMissingDataError';
}
generateObjectsData = sc.toJson(await FileHandler.fetchFileContents(
FileHandler.joinPaths(this.themeManager.projectGeneratedDataPath, fileName)
));
if(!generateObjectsData){
return this.rootPath+this.objectsImportPath+'?result=objectsImportDataError';
}
}
let importResult = await this.objectsImporter.import(generateObjectsData);
if(!importResult){
let errorCode = this.objectsImporter.errorCode || 'objectsImportError';
return this.rootPath+this.objectsImportPath+'?result='+errorCode;
}
return this.rootPath+this.objectsImportPath+'?result=success';
}
}
module.exports.ObjectsImporterSubscriber = ObjectsImporterSubscriber;
================================================
FILE: lib/admin/server/subscribers/rooms-entity-subscriber.js
================================================
/**
*
* Reldens - RoomsEntitySubscriber
*
* Subscriber that handles room-specific admin panel operations including setting default rooms
* and creating room transition links via change points and return points.
*
*/
const { RoomMapTilesetsValidator } = require('../room-map-tilesets-validator');
const { RoomsFileUploadRenderer } = require('../rooms-file-upload-renderer');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@reldens/cms/lib/admin-manager').AdminManager} AdminManager
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
* @typedef {import('../../../game/server/config-manager').ConfigManager} ConfigManager
* @typedef {import('express').Router} ExpressRouter
* @typedef {import('express').Request} ExpressRequest
* @typedef {import('express').Response} ExpressResponse
*/
class RoomsEntitySubscriber
{
/**
* @param {AdminManager} adminManager
* @param {ConfigManager} config
*/
constructor(adminManager, config)
{
/** @type {BaseDataServer} */
this.dataServer = adminManager.dataServer;
/** @type {EventsManager} */
this.events = adminManager.events;
/** @type {ConfigManager} */
this.config = config;
/** @type {Function} */
this.isAuthenticated = adminManager.router.isAuthenticated.bind(adminManager.router);
/** @type {Function} */
this.generateViewRouteContent = adminManager.routerContents.generateViewRouteContent.bind(
adminManager.routerContents
);
this.setupRepositories();
this.listenEvents();
}
/**
* @returns {boolean|void}
*/
setupRepositories()
{
if(!this.dataServer){
Logger.error('Data server is not defined for RoomsEntitySubscriber.');
return false;
}
/** @type {Object} */
this.configRepository = this.dataServer.getEntity('config');
/** @type {Object} */
this.roomsRepository = this.dataServer.getEntity('rooms');
/** @type {Object} */
this.roomsChangePointsRepository = this.dataServer.getEntity('roomsChangePoints');
/** @type {Object} */
this.roomsReturnPointsRepository = this.dataServer.getEntity('roomsReturnPoints');
/** @type {RoomMapTilesetsValidator} */
this.roomMapTilesetsValidator = new RoomMapTilesetsValidator(this.dataServer, this.config);
/** @type {RoomsFileUploadRenderer} */
this.fileUploadRenderer = new RoomsFileUploadRenderer();
}
/**
* @returns {boolean|void}
*/
listenEvents()
{
if(!this.events){
Logger.error('EventsManager is not defined for RoomsEntitySubscriber.');
return false;
}
this.events.on('adminEntityExtraData', async (event) => {
if('rooms' !== event.entityId){
return;
}
event.entitySerializedData.extraData = await this.roomsEntityExtraData();
});
this.events.on('reldens.setupEntitiesRoutes', (event) => {
if('rooms' !== event.entityPath){
return;
}
this.setupRoomsSpecificRoutes(
event.adminManager.router.adminRouter,
event.adminManager.rootPath,
event.adminManager.viewPath,
event.entityRoute,
event.driverResource
);
});
this.events.on('reldens.adminAfterEntitySave', async (event) => {
await this.roomMapTilesetsValidator.validate(event);
});
this.events.on('reldens.adminEditPropertiesPopulation', (event) => {
if('rooms' !== event.entityId){
return;
}
this.populateEditFormTilesetImages(event);
});
this.events.on('reldens.adminBeforeFieldRender', async (event) => {
await this.fileUploadRenderer.renderFileUploadField(event);
});
}
/**
* @param {ExpressRouter} adminRouter
* @param {string} rootPath
* @param {string} viewPath
* @param {string} entityRoute
* @param {Object} driverResource
* @returns {boolean|void}
*/
setupRoomsSpecificRoutes(adminRouter, rootPath, viewPath, entityRoute, driverResource)
{
if(!this.dataServer){
return false;
}
if(!adminRouter){
Logger.error('Admin Router is not defined for RoomsEntitySubscriber.');
return false;
}
adminRouter.post(entityRoute+viewPath, this.isAuthenticated, async (req, res) => {
let routeContents = await this.generateViewRouteContent(req, driverResource, 'rooms');
if('' === routeContents){
return res.redirect(rootPath+'/rooms?result=errorView');
}
let roomId = req.query.id;
if(!roomId){
Logger.warning('Missing entity ID on POST.', req.query);
return res.redirect(rootPath+'/rooms?result=errorId');
}
let setAsDefault = req.body.setAsDefault;
if('1' === setAsDefault){
let result = await this.configRepository.update(
{path: 'players/initialState/room_id'},
{value: roomId}
);
if(!result){
return res.redirect(
rootPath+'/rooms'+viewPath+'?id='+roomId+'&result=errorSaveDefault'
);
}
return res.redirect(rootPath+'/rooms'+viewPath+'?id='+roomId+'&result=success');
}
let createRoomsLink = req.body.createRoomsLink;
if('1' === createRoomsLink){
let currentRoomChangePointTileIndex = req.body.currentRoomChangePointTileIndex;
if(!currentRoomChangePointTileIndex){
return res.redirect(
rootPath+'/rooms'+viewPath+'?id='+roomId+'&result=errorMissingTileIndex'
);
}
let nextRoomId = req.body.nextRoomSelector;
if(!nextRoomId){
return res.redirect(
rootPath+'/rooms'+viewPath+'?id='+roomId+'&result=errorMissingNextRoom'
);
}
let nextRoomPositionX = req.body.nextRoomPositionX;
if(!nextRoomPositionX){
return res.redirect(
rootPath+'/rooms'+viewPath+'?id='+roomId+'&result=errorMissingRoomX'
);
}
let nextRoomPositionY = req.body.nextRoomPositionY;
if(!nextRoomPositionY){
return res.redirect(
rootPath+'/rooms'+viewPath+'?id='+roomId+'&result=errorMissingRoomY'
);
}
let nextRoomDirection = sc.get(req.body, 'nextRoomDirection', 'down');
let nextRoomIsDefault = sc.get(req.body, 'nextRoomIsDefault', '0');
let changePointResult = await this.roomsChangePointsRepository.create({
room_id: roomId,
tile_index: currentRoomChangePointTileIndex,
next_room_id: nextRoomId
});
if(!changePointResult){
return res.redirect(
rootPath+'/rooms'+viewPath+'?id='+roomId+'&result=errorSaveChangePoint'
);
}
let returnPointResult = await this.roomsReturnPointsRepository.create({
room_id: nextRoomId,
direction: nextRoomDirection,
x: nextRoomPositionX,
y: nextRoomPositionY,
is_default: '1' === nextRoomIsDefault,
from_room_id: roomId
});
if(!returnPointResult){
return res.redirect(
rootPath+'/rooms'+viewPath+'?id='+roomId+'&result=errorSaveReturnPoint'
);
}
return res.redirect(rootPath+'/rooms'+viewPath+'?id='+roomId+'&result=success');
}
return res.send(routeContents);
});
}
/**
* @returns {Promise}
*/
async roomsEntityExtraData()
{
let loadedRooms = await this.roomsRepository?.loadAll() || [];
return {
roomsList: loadedRooms.map((room) => {
return {id: room.id, name: room.name, mapFile: room.map_filename, mapImages: room.scene_images};
})
};
}
/**
* @param {Object} event
* @returns {boolean|void}
*/
populateEditFormTilesetImages(event)
{
let overrideEnabled = this.config.getWithoutLogs('server/rooms/maps/overrideSceneImagesWithMapFile', true);
if(!overrideEnabled){
return false;
}
let entityData = sc.get(event, 'entityData', false);
if(!entityData){
return false;
}
let driverResource = sc.get(event, 'driverResource', false);
if(!driverResource){
return false;
}
let tilesetImages = this.roomMapTilesetsValidator.extractTilesetImagesFromEntity(entityData, driverResource);
if(!tilesetImages || 0 === tilesetImages.length){
return false;
}
let renderedEditProperties = sc.get(event, 'renderedEditProperties', false);
if(!renderedEditProperties){
return false;
}
renderedEditProperties.tilesetImages = tilesetImages;
renderedEditProperties.overrideSceneImagesEnabled = overrideEnabled;
}
}
module.exports.RoomsEntitySubscriber = RoomsEntitySubscriber;
================================================
FILE: lib/admin/server/subscribers/shutdown-subscriber.js
================================================
/**
*
* Reldens - ShutdownSubscriber
*
* Subscriber that provides server shutdown functionality through the admin panel.
* Allows administrators to schedule server shutdown with countdown and cancellation support.
*
*/
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@reldens/cms/lib/admin-manager').AdminManager} AdminManager
* @typedef {import('../../../config/server/manager').ConfigManager} ConfigManager
*/
class ShutdownSubscriber
{
/**
* @param {AdminManager} adminManager
* @param {ConfigManager} configManager
* @param {Function} broadcastCallback
*/
constructor(adminManager, configManager, broadcastCallback)
{
/** @type {string} */
this.managementPath = '/management';
/** @type {string} */
this.rootPath = '';
/** @type {Function} */
this.broadcastCallback = broadcastCallback;
/** @type {Object|null} */
this.translations = null;
/** @type {number} */
this.defaultShutdownTime = 180;
/** @type {number} */
this.shutdownTime = 0;
/** @type {number} */
this.shuttingDownIn = 0;
/** @type {NodeJS.Timeout|null} */
this.shutdownInterval = null;
/** @type {NodeJS.Timeout|null} */
this.shutdownTimeout = null;
/** @type {ConfigManager} */
this.config = configManager;
/** @type {EventsManager} */
this.events = adminManager.events;
/** @type {Function} */
this.render = adminManager.contentsBuilder.render.bind(adminManager.contentsBuilder);
/** @type {Function} */
this.renderRoute = adminManager.contentsBuilder.renderRoute.bind(adminManager.contentsBuilder);
/** @type {Function} */
this.isAuthenticated = adminManager.router.isAuthenticated.bind(adminManager.router);
this.listenEvents();
this.fetchConfigurations();
}
/**
* @returns {boolean|void}
*/
listenEvents()
{
if(!this.events){
Logger.error('EventsManager not found on ShutdownSubscriber.');
return false;
}
this.events.on('reldens.setupAdminManagers', async (event) => {
this.setupRoutes(event.adminManager);
});
this.events.on('reldens.adminSideBarBeforeSubItems', async (event) => {
event.navigationContents['Server'] = {'Management': await this.render(
event.adminManager.adminFilesContents.sideBarItem,
{
name: event.adminManager.translations.labels['management'],
path: event.adminManager.rootPath+this.managementPath
}
)};
});
this.events.on('reldens.buildAdminContentsAfter', async (event) => {
let pageContent = await this.render(
event.adminManager.adminFilesContents.management,
{
actionPath: event.adminManager.rootPath+this.managementPath,
shutdownTime: this.config.getWithoutLogs('server/shutdownTime', this.defaultShutdownTime),
shuttingDownLabel: '{{&shuttingDownLabel}}',
shuttingDownTime: '{{&shuttingDownTime}}',
submitLabel: '{{&submitLabel}}',
submitType: '{{&submitType}}'
}
);
event.adminManager.contentsBuilder.adminContents.management = await this.renderRoute(
pageContent,
event.adminManager.contentsBuilder.adminContents.sideBar
);
});
}
/**
* @returns {boolean|void}
*/
fetchConfigurations()
{
if(!this.config){
return false;
}
/** @type {number} */
this.configuredShutdownTime = this.config.getWithoutLogs('server/shutdownTime', this.defaultShutdownTime);
}
/**
* @param {AdminManager} adminManager
* @returns {boolean|void}
*/
setupRoutes(adminManager)
{
if('' === this.rootPath){
this.rootPath = adminManager.rootPath;
}
if(!this.broadcastCallback){
this.broadcastCallback = adminManager.broadcastCallback;
}
if(!this.translations){
this.translations = adminManager.translations;
}
if(!adminManager.router.adminRouter){
Logger.error('AdminRouter is not available in ShutdownSubscriber.setupRoutes.');
return false;
}
adminManager.router.adminRouter.get(this.managementPath, this.isAuthenticated, async (req, res) => {
let rendererContent = await this.render(
adminManager.contentsBuilder.adminContents.management,
this.getShuttingDownData()
);
return res.send(rendererContent);
});
adminManager.router.adminRouter.post(this.managementPath, this.isAuthenticated, async (req, res) => {
this.shutdownTime = req.body['shutdown-time'];
let redirectManagementPath = this.rootPath+this.managementPath;
if(!this.shutdownTime){
return res.redirect(redirectManagementPath+'?result=shutdownError');
}
if(0 < this.shuttingDownIn){
clearInterval(this.shutdownInterval);
clearTimeout(this.shutdownTimeout);
this.shuttingDownIn = 0;
return res.redirect(redirectManagementPath+'?result=success');
}
await this.broadcastShutdownMessage();
this.shutdownTimeout = setTimeout(
async () => {
Logger.info('Server is shutting down by request on the administration panel.', sc.getTime());
if(this.broadcastCallback && sc.isFunction(this.broadcastCallback)){
await this.broadcastCallback({message: 'Server Offline.'});
}
throw new Error('Server shutdown by request on the administration panel.');
},
this.shutdownTime * 1000
);
this.shutdownInterval = setInterval(
async () => {
this.shuttingDownIn--;
Logger.info('Server is shutting down in '+this.shuttingDownIn+' seconds.');
if(
0 < this.shuttingDownIn
&& (this.shuttingDownIn <= 5 || Math.ceil(this.shutdownTime / 2) === this.shuttingDownIn)
){
await this.broadcastShutdownMessage();
}
if(0 === this.shuttingDownIn){
Logger.info('Server OFF at: '+ sc.getTime());
clearInterval(this.shutdownInterval);
}
},
1000
);
this.shuttingDownIn = this.shutdownTime;
return res.redirect(redirectManagementPath+'?result=success');
});
}
/**
* @returns {Object}
*/
getShuttingDownData()
{
if(0 === this.shuttingDownIn){
return {
shuttingDownLabel: '',
shuttingDownTime: '',
submitLabel: this.translations.labels.submitShutdownLabel || 'Shutdown Server',
submitType: 'danger',
shutdownTime: this.configuredShutdownTime
};
}
return {
shuttingDownLabel: this.translations.labels.shuttingDown || '',
shuttingDownTime: this.shuttingDownIn || '',
submitLabel: this.translations.labels.submitCancelLabel || 'Cancel Server Shutdown',
submitType: 'warning',
shutdownTime: this.configuredShutdownTime
};
}
/**
* @returns {Promise}
*/
async broadcastShutdownMessage()
{
let shuttingDownTime = 0 === this.shuttingDownIn ? this.shutdownTime : this.shuttingDownIn;
await this.broadcastSystemMessage('Server is shutting down in ' + shuttingDownTime + ' seconds.');
}
/**
* @param {string} message
* @returns {Promise}
*/
async broadcastSystemMessage(message)
{
if(!this.broadcastCallback || !sc.isFunction(this.broadcastCallback)){
Logger.warning('Broadcast callback was not configured in ShutdownSubscriber.', this.broadcastCallback);
return;
}
await this.broadcastCallback({message});
}
}
module.exports.ShutdownSubscriber = ShutdownSubscriber;
================================================
FILE: lib/admin/server/subscribers/skills-importer-subscriber.js
================================================
/**
*
* Reldens - SkillsImporterSubscriber
*
* Subscriber that handles skills import functionality in the admin panel.
* Allows importing skills data from JSON files or direct form input.
*
*/
const { SkillsImporter } = require('../../../import/server/skills-importer');
const { FileHandler } = require('@reldens/server-utils');
const { AllowedFileTypes } = require('../../../game/allowed-file-types');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@reldens/cms/lib/admin-manager').AdminManager} AdminManager
* @typedef {import('../../../game/server/theme-manager').ThemeManager} ThemeManager
* @typedef {import('express').Request} ExpressRequest
*/
class SkillsImporterSubscriber
{
/**
* @param {AdminManager} adminManager
* @param {ThemeManager} themeManager
*/
constructor(adminManager, themeManager)
{
/** @type {string} */
this.skillsImportPath = '/skills-import';
/** @type {string} */
this.rootPath = '';
/** @type {ThemeManager} */
this.themeManager = themeManager;
/** @type {EventsManager} */
this.events = adminManager.events;
/** @type {Function} */
this.render = adminManager.contentsBuilder.render.bind(adminManager.contentsBuilder);
/** @type {Function} */
this.renderRoute = adminManager.contentsBuilder.renderRoute.bind(adminManager.contentsBuilder);
/** @type {Function} */
this.isAuthenticated = adminManager.router.isAuthenticated.bind(adminManager.router);
/** @type {Array} */
this.fields = [{name: 'generatorJsonFiles'}];
/** @type {Object} */
this.buckets = {generatorJsonFiles: this.themeManager.projectGeneratedDataPath};
/** @type {Object} */
this.allowedFileTypes = {generatorJsonFiles: AllowedFileTypes.TEXT};
/** @type {Function} */
this.uploader = adminManager.uploaderFactory.createUploader(this.fields, this.buckets, this.allowedFileTypes);
/** @type {SkillsImporter} */
this.skillsImporter = new SkillsImporter(adminManager);
this.listenEvents();
}
/**
* @returns {boolean|void}
*/
listenEvents()
{
if(!this.events){
Logger.error('EventsManager not found on SkillsImporterSubscriber.');
return false;
}
this.events.on('reldens.setupAdminManagers', async (event) => {
this.setupRoutes(event.adminManager);
});
this.events.on('reldens.eventBuildSideBarBefore', async (event) => {
if(!event.navigationContents['Wizards']){
event.navigationContents['Wizards'] = {};
}
let translatedLabel = event.adminManager.translations.labels['skillsImport'];
event.navigationContents['Wizards'][translatedLabel] = await event.adminManager.contentsBuilder.render(
event.adminManager.adminFilesContents.sideBarItem,
{name: translatedLabel, path: event.adminManager.rootPath+this.skillsImportPath}
);
});
this.events.on('reldens.buildAdminContentsAfter', async (event) => {
let pageContent = await this.render(
event.adminManager.adminFilesContents.skillsImport,
{actionPath: event.adminManager.rootPath+this.skillsImportPath}
);
event.adminManager.contentsBuilder.adminContents.skillsImport = await this.renderRoute(
pageContent,
event.adminManager.contentsBuilder.adminContents.sideBar
);
});
}
/**
* @param {AdminManager} adminManager
*/
setupRoutes(adminManager)
{
if('' === this.rootPath){
this.rootPath = adminManager.rootPath;
}
adminManager.router.adminRouter.get(this.skillsImportPath, this.isAuthenticated, async (req, res) => {
return res.send(await this.render(adminManager.contentsBuilder.adminContents.skillsImport));
});
adminManager.router.adminRouter.post(
this.skillsImportPath,
this.isAuthenticated,
this.uploader,
async (req, res) => {
return res.redirect(await this.importSkills(req));
}
);
}
/**
* @param {ExpressRequest} req
* @returns {Promise}
*/
async importSkills(req)
{
let generateSkillsData = sc.toJson(req?.body?.generatorData);
if(!generateSkillsData){
let fileName = req.files?.generatorJsonFiles?.shift()?.originalname;
if(!fileName){
return this.rootPath+this.skillsImportPath+'?result=skillsImportMissingDataError';
}
generateSkillsData = sc.toJson(await FileHandler.fetchFileContents(
FileHandler.joinPaths(this.themeManager.projectGeneratedDataPath, fileName)
));
if(!generateSkillsData){
return this.rootPath+this.skillsImportPath+'?result=skillsImportDataError';
}
}
let importResult = await this.skillsImporter.import(generateSkillsData);
if(!importResult){
let errorCode = this.skillsImporter.errorCode || 'skillsImportError';
return this.rootPath+this.skillsImportPath+'?result='+errorCode;
}
return this.rootPath+this.skillsImportPath+'?result=success';
}
}
module.exports.SkillsImporterSubscriber = SkillsImporterSubscriber;
================================================
FILE: lib/admin/server/subscribers/theme-manager-subscriber.js
================================================
/**
*
* Reldens - ThemeManagerSubscriber
*
* Subscriber that provides theme management functionality through the admin panel.
* Allows administrators to execute ThemeManager commands on selected themes.
*
*/
const { Logger, sc } = require('@reldens/utils');
const { FileHandler } = require('@reldens/server-utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@reldens/cms/lib/admin-manager').AdminManager} AdminManager
* @typedef {import('../../../config/server/manager').ConfigManager} ConfigManager
* @typedef {import('../../../game/server/theme-manager').ThemeManager} ThemeManager
*/
class ThemeManagerSubscriber
{
/**
* @param {AdminManager} adminManager
* @param {ConfigManager} configManager
* @param {ThemeManager} themeManager
*/
constructor(adminManager, configManager, themeManager)
{
/** @type {string} */
this.themeManagerPath = '/theme-manager';
/** @type {string} */
this.managementPath = '/management';
/** @type {string} */
this.serverManagerLabel = 'Server Manager';
/** @type {number} */
this.defaultShutdownTime = 180;
/** @type {string} */
this.rootPath = '';
/** @type {ThemeManager} */
this.themeManager = themeManager;
/** @type {ConfigManager} */
this.config = configManager;
/** @type {Object|null} */
this.translations = null;
/** @type {EventsManager} */
this.events = adminManager.events;
/** @type {Function} */
this.render = adminManager.contentsBuilder.render.bind(adminManager.contentsBuilder);
/** @type {Function} */
this.renderRoute = adminManager.contentsBuilder.renderRoute.bind(adminManager.contentsBuilder);
/** @type {Function} */
this.isAuthenticated = adminManager.router.isAuthenticated.bind(adminManager.router);
/** @type {Object} */
this.commands = this.getCommandsMetadata();
this.listenEvents();
}
/**
* @returns {Object}
*/
getCommandsMetadata()
{
return {
buildCss: {
category: 'build',
label: 'Build CSS',
async: true,
description: 'Compile SCSS to CSS using Parcel bundler',
details: 'Enabled by default. Set RELDENS_ALLOW_BUILD_CSS=0 to disable. Creates optimized CSS in dist/css/.'
},
buildClient: {
category: 'build',
label: 'Build Client',
async: true,
description: 'Bundle client JavaScript using Parcel',
details: 'Enabled by default. Set RELDENS_ALLOW_BUILD_CLIENT=0 to disable. Processes all .html files in theme folder.'
},
buildSkeleton: {
category: 'build',
label: 'Build Skeleton',
async: true,
description: 'Build both CSS and client',
details: 'Combined build operation for complete theme compilation.'
},
resetDist: {
category: 'clientDist',
label: 'Reset Dist',
async: false,
description: 'Remove and recreate dist folder',
details: 'Deletes dist/ completely and creates fresh empty structure.'
},
removeDist: {
category: 'clientDist',
label: 'Remove Dist',
async: false,
description: 'Delete dist folder',
details: 'Permanently removes dist/ folder.'
},
copyAssetsToDist: {
category: 'clientDist',
label: 'Copy Assets to Dist',
async: false,
description: 'Copy theme assets folder to dist/assets',
details: 'Copies images, audio, and other assets from theme to dist.'
},
copyDefaultAssets: {
category: 'clientDist',
label: 'Copy Default Assets',
async: false,
description: 'Copy default theme assets to dist',
details: 'Copies assets from default theme.'
},
copyDefaultTheme: {
category: 'copy',
label: 'Copy Default Theme',
async: false,
description: 'Copy default theme files to project theme',
details: 'Overwrites current theme with default theme files.'
},
copyPackage: {
category: 'copy',
label: 'Copy Plugins',
async: false,
description: 'Copy theme plugins folder',
details: 'Copies plugin files from default theme.'
},
copyAdmin: {
category: 'copy',
label: 'Copy Admin',
async: false,
description: 'Copy admin panel files',
details: 'Copies admin templates and assets.'
},
copyAdminFiles: {
category: 'clientDist',
label: 'Copy Admin Files to Dist',
async: false,
description: 'Copy admin JS/CSS to dist',
details: 'Copies functions.js and reldens-functions.js to dist/js/, admin.js to dist/, admin.css to dist/css/.'
},
copyNew: {
category: 'copy',
label: 'Copy New (All)',
async: false,
description: 'Copy all theme files',
details: 'Runs: copyDefaultAssets, copyDefaultTheme, copyPackage, copyAdmin.'
},
copyIndex: {
category: 'install',
label: 'Copy Index',
async: true,
description: 'Generate index.js from template',
details: 'Creates project index.js with theme configuration. Skips if index.js already exists.'
},
copyServerFiles: {
category: 'install',
label: 'Copy Server Files',
async: true,
description: 'Copy server configuration files',
details: 'Runs: copyEnvFile, copyKnexFile, copyGitignoreFile, copyIndex.'
},
installSkeleton: {
category: 'install',
label: 'Install Skeleton',
async: true,
description: 'Complete skeleton installation',
details: 'Runs: copyIndex, copyServerFiles, resetDist, fullRebuild.'
},
fullRebuild: {
category: 'install',
label: 'Full Rebuild',
async: true,
description: 'Complete rebuild of theme',
details: 'Runs: copyNew, buildSkeleton, copyAdminFiles. Takes 30-60 seconds.'
},
};
}
/**
* @returns {boolean|void}
*/
listenEvents()
{
if(!this.events){
Logger.error('EventsManager not found on ThemeManagerSubscriber.');
return false;
}
this.events.on('reldens.setupAdminManagers', async (event) => {
this.setupRoutes(event.adminManager);
});
this.events.on('reldens.adminSideBarBeforeSubItems', async (event) => {
event.navigationContents['Server'] = await this.render(
event.adminManager.adminFilesContents.sideBarItem,
{
name: this.serverManagerLabel,
path: event.adminManager.rootPath+this.managementPath
}
);
});
this.events.on('reldens.buildAdminContentsAfter', async (event) => {
let rootPath = event.adminManager.rootPath;
let managementBody = await this.render(
event.adminManager.adminFilesContents.management,
{
actionPath: rootPath+this.managementPath,
shutdownTime: this.config.getWithoutLogs(
'server/shutdownTime',
this.defaultShutdownTime
),
shuttingDownLabel: '{{&shuttingDownLabel}}',
shuttingDownTime: '{{&shuttingDownTime}}',
submitLabel: '{{&submitLabel}}',
submitType: '{{&submitType}}'
}
);
let themeBody = await this.render(
event.adminManager.adminFilesContents.themeManager,
this.getTemplateData(event.adminManager)
);
event.adminManager.contentsBuilder.adminContents.management = await this.renderRoute(
managementBody+themeBody,
event.adminManager.contentsBuilder.adminContents.sideBar
);
});
}
/**
* @param {AdminManager} adminManager
* @returns {boolean|void}
*/
setupRoutes(adminManager)
{
if('' === this.rootPath){
this.rootPath = adminManager.rootPath;
}
if(!this.translations){
this.translations = adminManager.translations;
}
if(!adminManager.router.adminRouter){
Logger.error('AdminRouter is not available in ThemeManagerSubscriber.setupRoutes.');
return false;
}
adminManager.router.adminRouter.post(
this.themeManagerPath,
this.isAuthenticated,
async (req, res) => {
let selectedTheme = req.body['selected-theme'];
let command = req.body['command'];
let redirectPath = this.rootPath+this.managementPath;
if(!selectedTheme){
return res.redirect(redirectPath+'?result=themeManagerMissingTheme');
}
if(!command || !this.commands[command]){
return res.redirect(redirectPath+'?result=themeManagerMissingCommand');
}
try {
await this.executeCommand(command, selectedTheme);
return res.redirect(redirectPath+'?result=success');
} catch(error){
Logger.error('Theme manager command failed', {command, theme: selectedTheme, error});
return res.redirect(redirectPath+'?result=themeManagerExecutionError');
}
}
);
}
/**
* @param {string} commandName
* @param {string} themeName
* @returns {Promise}
*/
async executeCommand(commandName, themeName)
{
Logger.info('Executing theme manager command: '+commandName+' on theme: '+themeName);
this.themeManager.setupPaths({
projectRoot: this.themeManager.projectRoot,
projectThemeName: themeName
});
if(!sc.isFunction(this.themeManager[commandName])){
throw new Error('Invalid command: '+commandName);
}
await this.themeManager[commandName]();
Logger.info('Theme manager command completed: '+commandName);
}
/**
* @returns {Array}
*/
getAvailableThemes()
{
try {
let themePath = this.themeManager.themePath;
let folders = FileHandler.fetchSubFoldersList(themePath);
let excludeFolders = ['admin', 'plugins'];
let themes = [];
for(let folder of folders){
if(excludeFolders.includes(folder)){
continue;
}
themes.push({
name: folder,
selected: folder === this.themeManager.projectThemeName
});
}
return themes;
} catch(error){
Logger.error('Failed to get available themes', error);
return [{name: 'default', selected: true}];
}
}
/**
* @param {AdminManager} adminManager
* @returns {Object}
*/
getTemplateData(adminManager)
{
let rootPath = adminManager ? adminManager.rootPath : this.rootPath;
let themes = this.getAvailableThemes();
let buildCommands = [];
let clientDistCommands = [];
let copyCommands = [];
let installCommands = [];
let commandDescriptions = {};
for(let commandName of Object.keys(this.commands)){
let meta = this.commands[commandName];
let commandData = {
name: commandName,
label: meta.label,
async: meta.async
};
commandDescriptions[commandName] = {
description: meta.description,
details: meta.details
};
if('build' === meta.category){
buildCommands.push(commandData);
continue;
}
if('clientDist' === meta.category){
clientDistCommands.push(commandData);
continue;
}
if('copy' === meta.category){
copyCommands.push(commandData);
continue;
}
if('install' === meta.category){
installCommands.push(commandData);
}
}
return {
actionPath: rootPath+this.themeManagerPath,
currentTheme: this.themeManager.projectThemeName,
themes,
buildCommands,
clientDistCommands,
copyCommands,
installCommands,
commandDescriptionsJson: JSON.stringify(commandDescriptions)
};
}
}
module.exports.ThemeManagerSubscriber = ThemeManagerSubscriber;
================================================
FILE: lib/admin/server/templates-list.js
================================================
/**
*
* Reldens - TemplatesList
*
* Mapping of template keys to their corresponding HTML file names for the admin panel.
* Includes main page templates, layout components, and field rendering templates for view and edit modes.
*
*/
module.exports.TemplatesList = {
login: 'login.html',
dashboard: 'dashboard.html',
management: 'management.html',
themeManager: 'theme-manager.html',
mapsWizard: 'maps-wizard.html',
mapsWizardMapsSelection: 'maps-wizard-maps-selection.html',
objectsImport: 'objects-import.html',
skillsImport: 'skills-import.html',
list: 'list.html',
listContent: 'list-content.html',
view: 'view.html',
edit: 'edit.html',
layout: 'layout.html',
sideBar: 'sidebar.html',
sideBarHeader: 'sidebar-header.html',
sideBarItem: 'sidebar-item.html',
paginationLink: 'pagination-link.html',
defaultCopyRight: 'default-copyright.html',
fields: {
view: {
audio: 'audio.html',
audios: 'audios.html',
text: 'text.html',
textarea: 'textarea.html',
image: 'image.html',
images: 'images.html',
link: 'link.html',
links: 'links.html',
boolean: 'boolean.html'
},
edit: {
text: 'text.html',
textarea: 'textarea.html',
select: 'select.html',
checkbox: 'checkbox.html',
boolean: 'checkbox.html',
radio: 'radio.html',
button: 'button.html',
file: 'file.html'
}
},
sections: {
view: {
rooms: 'rooms.html'
}
}
};
================================================
FILE: lib/ads/client/ads-provider.js
================================================
/**
*
* Reldens - AdsProvider
*
* Utility class for filtering and fetching active ads by provider ID and type.
*
*/
const { Logger } = require('@reldens/utils');
class AdsProvider
{
/**
* @param {number} providerId
* @param {Array} validAdsTypes
* @param {Object} availableAds
* @returns {Object}
*/
static fetchActiveAdsByProviderId(providerId, validAdsTypes, availableAds)
{
if(!providerId){
return {};
}
let adsKeys = Object.keys(availableAds);
if(0 === adsKeys.length){
return {};
}
let adsCollection = {};
for(let i of adsKeys){
let ad = availableAds[i];
if(providerId !== ad.provider.id){
// Logger.info('Filtered ad by provider ID.', {expectedId: providerId, adProviderId: ad.provider.id});
continue;
}
if(!ad.enabled){
Logger.info('Ad not enabled.', ad);
continue;
}
if(-1 === validAdsTypes.indexOf(ad.type.key)){
Logger.info('Invalid ad type.', ad);
continue;
}
adsCollection[i] = ad;
}
Logger.info({providerId, activeProviderAds: adsCollection});
return adsCollection;
}
}
module.exports.AdsProvider = AdsProvider;
================================================
FILE: lib/ads/client/messages-listener.js
================================================
/**
*
* Reldens - MessagesListener
*
* Handles incoming messages from the server related to ads and updates the ads plugin state.
*
*/
const { AdsConst } = require('../constants');
const { Logger } = require('@reldens/utils');
/**
* @typedef {import('@colyseus/core').Room} Room
* @typedef {import('./plugin').AdsPlugin} AdsPlugin
*/
class MessagesListener
{
/**
* @param {Room} room
* @param {AdsPlugin} adsPlugin
* @returns {Promise}
*/
static async listenMessages(room, adsPlugin)
{
room.onMessage('*', (message) => {
if(AdsConst.ACTIONS.ADS_PLAYED !== message.act){
return false;
}
adsPlugin.playedAds = {};
if(!message.playedAdsModels){
Logger.info('None played ads.', message);
return false;
}
for(let playedAd of message.playedAdsModels){
adsPlugin.playedAds[playedAd.ads_id] = playedAd;
}
return true;
});
}
}
module.exports.MessagesListener = MessagesListener;
================================================
FILE: lib/ads/client/plugin.js
================================================
/**
*
* Reldens - AdsPlugin
*
* Client-side ads plugin that manages ad providers, SDK initialization, and ad playback events.
*
*/
const { MessagesListener } = require('./messages-listener');
const { SdkHandler } = require('./sdk-handler');
const { ProvidersList } = require('./providers-list');
const Translations = require('./snippets/en_US');
const { TranslationsMapper } = require('../../snippets/client/translations-mapper');
const { AdsConst } = require('../constants');
const { PluginInterface } = require('../../features/plugin-interface');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('./sdk-handler').SdkHandler} SdkHandler
*
* @typedef {Object} AdsPluginProps
* @property {GameManager} [gameManager] - The game manager instance
* @property {EventsManager} [events] - The events manager instance
*/
class AdsPlugin extends PluginInterface
{
/**
* @param {AdsPluginProps} props
*/
setup(props)
{
/** @type {GameManager|false} */
this.gameManager = sc.get(props, 'gameManager', false);
if(!this.gameManager){
Logger.error('Game Manager undefined in AdsPlugin.');
}
/** @type {EventsManager|false} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in AdsPlugin.');
}
/** @type {Object|false} */
this.config = {};
/** @type {Object} */
this.activeProviders = {};
/** @type {Object|null} */
this.playedAds = null;
this.setConfig();
this.setSkdHandler();
this.fetchActiveProviders();
this.setTranslations();
this.listenEvents();
}
setConfig()
{
this.config = this.gameManager ? this.gameManager.config.get('client/ads/general', {}) : false;
}
setSkdHandler()
{
let gameDom = this.gameManager?.gameDom;
/** @type {SdkHandler|false} */
this.sdkHandler = gameDom ? new SdkHandler({gameDom, config: this.config}) : false;
}
/**
* @returns {boolean}
*/
fetchActiveProviders()
{
let providers = sc.get(this.config, 'providers', {});
let providersKeys = Object.keys(providers);
if(0 === providersKeys.length){
//Logger.debug('None ads providers configured.', this.config);
return false;
}
for(let i of providersKeys){
let provider = providers[i];
if(!provider.enabled){
//Logger.debug({'Provider disabled': providers});
continue;
}
provider.classDefinition = sc.get(ProvidersList, i, false);
this.activeProviders[i] = provider;
}
}
/**
* @returns {boolean}
*/
setTranslations()
{
if(!this.gameManager){
return false;
}
TranslationsMapper.forConfig(this.gameManager.config.client, Translations, AdsConst.MESSAGE.DATA_VALUES);
}
/**
* @returns {boolean}
*/
listenEvents()
{
if(!this.events || !this.gameManager || !this.sdkHandler){
Logger.error('Missing properties for AdsPlugin.');
return false;
}
this.events.on('reldens.beforeCreateEngine', async (initialGameData, gameManager) => {
if(!this.sdkHandler){
Logger.info('Undefined SDK Handler.');
return;
}
await this.sdkHandler.setupProvidersSdk(this.activeProviders, gameManager);
});
this.events.on('reldens.joinedRoom', async (room) => {
await MessagesListener.listenMessages(room, this);
});
}
}
module.exports.AdsPlugin = AdsPlugin;
================================================
FILE: lib/ads/client/providers/crazy-games/banners-handler.js
================================================
/**
*
* Reldens - BannersHandler
*
* Manages banner ad creation, placement, and lifecycle for the CrazyGames SDK.
*
*/
const { Validator } = require('./validator');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../../game/client/game-manager').GameManager} GameManager
* @typedef {import('./validator').Validator} Validator
*/
/**
* @typedef {Object} BannersHandlerProps
* @property {GameManager} [gameManager] - The game manager instance
* @property {Object} [metaData] - Provider metadata configuration
* @property {Object} [sdk] - The CrazyGames SDK instance
* @property {Function} [hasAdblock] - Function to check for ad blocker
* @property {Function} [isEnabled] - Function to check if SDK is enabled
*/
class BannersHandler
{
/**
* @param {BannersHandlerProps} props
*/
constructor(props)
{
/** @type {GameManager|false} */
this.gameManager = sc.get(props, 'gameManager', false);
/** @type {Object} */
this.metaData = sc.get(props, 'metaData', {});
/** @type {Object} */
this.gameDom = this.gameManager?.gameDom;
/** @type {Object} */
this.events = this.gameManager?.events;
/** @type {Object|false} */
this.sdk = sc.get(props, 'sdk', false);
/** @type {Function|false} */
this.hasAdblock = sc.get(props, 'hasAdblock', false);
/** @type {Function|false} */
this.isEnabled = sc.get(props, 'isEnabled', false);
/** @type {Object} */
this.activeBanners = {};
/** @type {Validator} */
this.validator = new Validator();
}
/**
* @returns {Array}
*/
availableBanners()
{
return [
'728x90',
'300x250',
'320x50',
'468x60',
'320x100'
];
}
/**
* @returns {Array}
*/
availableResponsiveBanners()
{
return [
'970x90',
'320x50',
'160x600',
'336x280',
'728x90',
'300x600',
'468x60',
'970x250',
'300x250',
'250x250',
'120x600'
];
}
/**
* @param {string} size
* @returns {boolean}
*/
validBannerSize(size)
{
return -1 !== this.availableBanners().indexOf(size);
}
/**
* @param {string} size
* @returns {boolean}
*/
validResponsiveBannerSize(size)
{
return -1 !== this.availableResponsiveBanners().indexOf(size);
}
/**
* @param {Object} activeAd
* @returns {Promise}
*/
async activateAdBanner(activeAd)
{
if(!activeAd){
Logger.info('Missing activate ad.', activeAd);
return false;
}
if(!this.validator.validate(this)){
Logger.info('Invalid banner.');
return false;
}
let bannerData = activeAd.bannerData;
if(!bannerData){
Logger.info('No banner data.');
return false;
}
let isFullTimeBanner = sc.get(bannerData, 'fullTime', false);
let isResponsive = sc.get(bannerData, 'responsive', false);
if(isFullTimeBanner){
return await this.handleBannerType(isResponsive, activeAd);
}
let uiReferenceIds = sc.get(bannerData, 'uiReferenceIds', []);
if(0 === uiReferenceIds.length){
Logger.warning('Missing banner reference ID.');
return false;
}
this.events.on('reldens.openUI', async (event) => {
if(-1 !== uiReferenceIds.indexOf('ANY') || -1 !== uiReferenceIds.indexOf(event.openButton.id)){
let bannerLocalStorageKey = activeAd.id+'-'+event.openButton.id;
let createdAt = (new Date()).getTime();
let activeBanner = sc.get(this.activeBanners, bannerLocalStorageKey, false);
if(activeBanner && createdAt < ( // create time is bigger than the previous created banner + 60s?
activeBanner.createdAt + this.metaData.sdkBannerRefreshTime
)){
activeBanner.banner.classList.remove('hidden');
return;
}
if(activeBanner){
activeBanner.banner.remove();
}
let banner = await this.handleBannerType(isResponsive, activeAd, bannerLocalStorageKey);
this.activeBanners[bannerLocalStorageKey] = {createdAt, banner};
}
});
this.events.on('reldens.closeUI', async (event) => {
let bannerLocalStorageKey = activeAd.id+'-'+event.openButton.id;
let activeBanner = sc.get(this.activeBanners, bannerLocalStorageKey, false);
if(activeBanner){
activeBanner.banner.classList.add('hidden');
}
});
}
/**
* @param {boolean} isResponsive
* @param {Object} activeAd
* @param {string} [bannerLocalStorageKey]
* @returns {Promise}
*/
async handleBannerType(isResponsive, activeAd, bannerLocalStorageKey)
{
if(isResponsive){
return this.createResponsiveBanner(activeAd, bannerLocalStorageKey);
}
return await this.createBanner(activeAd, bannerLocalStorageKey);
}
/**
* @param {Object} activeAd
* @param {string} [bannerLocalStorageKey]
* @returns {Promise}
*/
async createBanner(activeAd, bannerLocalStorageKey)
{
if(!this.validator.validate(this) || !await this.validator.canBeActivated(this)){
return false;
}
if(!this.isEnabled()){
Logger.info('SDK not enabled.');
return false;
}
try {
let width = sc.get(activeAd.styles, 'width', '300');
let height = sc.get(activeAd.styles, 'height', '250');
if(!this.validBannerSize(width+'x'+height)){
Logger.info('CrazyGames - Invalid Banner size.');
return false;
}
let containerId = bannerLocalStorageKey || activeAd.id;
if(!containerId){
Logger.info('CrazyGames - Missing container ID.', activeAd, bannerLocalStorageKey);
return false;
}
let div = this.gameDom.createElement('div', 'banner-container-'+containerId);
this.gameDom.getElement('body')?.append(div);
if(await this.isEnabled()){
await this.sdk.banner.requestBanner({
id: div.id,
width: width,
height: height,
});
}
let styles = this.mapStylesWithValues(Object.assign({width, height}, activeAd));
this.gameDom.setElementStyles(div, styles);
div.classList.add('ads-banner-container');
return div;
} catch (error) {
Logger.critical('CrazyGames - Error on banner request.', error);
return false;
}
}
/**
* @param {Object} activeAd
* @returns {Object}
*/
mapStylesWithValues(activeAd)
{
let styles = {
'z-index': 200000000,
width: sc.get(activeAd, 'width', 300),
height: sc.get(activeAd, 'height', 250),
position: '' === sc.get(activeAd.styles, 'position', '') ? activeAd.position : 'absolute'
};
let top = sc.get(activeAd.styles, 'top', null);
if(null !== top){
styles.top = top;
}
let bottom = sc.get(activeAd.styles, 'bottom', null);
if(null !== bottom){
styles.bottom = bottom;
}
let left = sc.get(activeAd.styles, 'left', null);
if(null !== left){
styles.left = left;
}
let right = sc.get(activeAd.styles, 'right', null);
if(null !== right){
styles.right = right;
}
return styles;
}
/**
* @param {Object} activeAd
* @param {string} [bannerLocalStorageKey]
* @returns {Promise}
*/
async createResponsiveBanner(activeAd, bannerLocalStorageKey)
{
if(!this.validator.validate(this) || !await this.validator.canBeActivated(this)){
return false;
}
if(!this.isEnabled()){
Logger.info('SDK not enabled.');
return false;
}
/* @NOTE: according to CrazyGames SDK this should be null and provided on the SDK response.
let width = sc.get(activeAd, 'width', '300');
let height = sc.get(activeAd, 'height', '250');
if(!this.validResponsiveBannerSize(width+'x'+height)){
Logger.info('CrazyGames - Invalid Responsive Banner size.');
return false;
}
*/
try {
let containerId = bannerLocalStorageKey || activeAd.id;
if(!containerId){
Logger.info('CrazyGames - Missing container ID.', activeAd, bannerLocalStorageKey);
return false;
}
let div = this.gameDom.createElement('div', 'responsive-banner-container-'+containerId);
let styles = this.mapStylesWithValues(activeAd);
delete styles['width'];
delete styles['height'];
this.gameDom.setElementStyles(div, styles);
this.gameDom.getElement('body').append(div);
if(await this.isEnabled()){
await this.sdk.banner.requestResponsiveBanner(div.id);
}
div.classList.add('ads-banner-container');
return div;
} catch (error) {
Logger.critical('CrazyGames - Error on banner request.', error);
return false;
}
}
}
module.exports.BannersHandler = BannersHandler;
================================================
FILE: lib/ads/client/providers/crazy-games/validator.js
================================================
/**
*
* Reldens - Validator
*
* Validates the required properties for ad activation in the CrazyGames SDK.
*
*/
const { Logger, sc } = require('@reldens/utils');
class Validator
{
/**
* @param {Object} props
* @returns {boolean}
*/
validate(props)
{
if(!props.gameManager){
Logger.error('Missing Game Manager on Validator.', props);
return false;
}
if(!props.sdk){
Logger.error('Missing SDK on Validator.', props);
return false;
}
if(!props.hasAdblock || !sc.isFunction(props.hasAdblock)){
Logger.warning('Missing or invalid hasAdblock function on Validator.', props);
}
if(!props.isEnabled || !sc.isFunction(props.isEnabled)){
Logger.error('Missing or invalid isEnabled function on Validator.', props);
return false;
}
return true;
}
/**
* @param {Object} props
* @returns {Promise}
*/
async canBeActivated(props)
{
if(!sc.isFunction(props.hasAdblock) || await props.hasAdblock()){
Logger.info('AdBlocker detected.');
return false;
}
return true;
}
}
module.exports.Validator = Validator;
================================================
FILE: lib/ads/client/providers/crazy-games/videos-handler.js
================================================
/**
*
* Reldens - VideosHandler
*
* Manages video ad playback and rewards for the CrazyGames SDK.
*
*/
const { Validator } = require('./validator');
const { AdsConst } = require('../../../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../../game/client/game-manager').GameManager} GameManager
* @typedef {import('./validator').Validator} Validator
*
* @typedef {Object} VideosHandlerProps
* @property {GameManager} [gameManager] - The game manager instance
* @property {Object} [sdk] - The CrazyGames SDK instance
* @property {Function} [hasAdblock] - Function to check for ad blocker
* @property {Function} [isEnabled] - Function to check if SDK is enabled
*/
class VideosHandler
{
/**
* @param {VideosHandlerProps} props
*/
constructor(props)
{
/** @type {GameManager|false} */
this.gameManager = sc.get(props, 'gameManager', false);
/** @type {Object} */
this.gameDom = this.gameManager?.gameDom;
/** @type {Object} */
this.events = this.gameManager?.events;
/** @type {Object|false} */
this.sdk = sc.get(props, 'sdk', false);
/** @type {Function|false} */
this.hasAdblock = sc.get(props, 'hasAdblock', false);
/** @type {Function|false} */
this.isEnabled = sc.get(props, 'isEnabled', false);
/** @type {Validator} */
this.validator = new Validator();
/** @type {boolean} */
this.isPlayingAd = false;
this.setConfig();
}
setConfig()
{
/** @type {number} */
this.videoMinimumDuration = !this.gameManager
? AdsConst.VIDEOS_MINIMUM_DURATION
: this.gameManager.config.getWithoutLogs(
'client/ads/general/providers/crazyGames/videoMinimumDuration',
AdsConst.VIDEOS_MINIMUM_DURATION
);
/** @type {number} */
this.awaitAdsTime = !this.gameManager
? AdsConst.AWAIT_ADS_TIME
: this.gameManager.config.getWithoutLogs(
'client/ads/general/providers/crazyGames/awaitAdsTime',
AdsConst.AWAIT_ADS_TIME
);
}
/**
* @param {Object} activeAd
* @returns {Promise}
*/
async activateAdVideo(activeAd)
{
let eventKey = sc.get(activeAd, 'eventKey', false);
if(!eventKey){
Logger.warning('Missing event key.', activeAd);
return false;
}
this.events.on(eventKey, async (event) => {
if(this.isPlayingAd){
Logger.info('CrazyGames - Another ad is been played.');
return false;
}
if(!this.validator.validate(this) || !await this.validator.canBeActivated(this)){
Logger.error('CrazyGames - Ad can not be activated.');
return false;
}
if(!this.isEnabled()){
Logger.info('CrazyGames - SDK not enabled.');
return false;
}
return await this.tryRePlay(activeAd);
});
}
/**
* @param {Object} activeAd
* @returns {Promise}
*/
async tryRePlay(activeAd)
{
let adsPlugin = this.gameManager.getFeature('ads');
if(null === adsPlugin.playedAds){
setTimeout(
() => {
this.tryRePlay(activeAd);
},
this.awaitAdsTime
);
return false;
}
let playedAd = sc.get(adsPlugin?.playedAds, activeAd.id, false);
if(playedAd && !activeAd.replay){
Logger.info('Ad already played', activeAd);
return false;
}
let adStarted = sc.get(activeAd, 'adStartedCallback', () => {
this.isPlayingAd = true;
Logger.info('CrazyGames - Ad-started callback.', (new Date()).getTime());
this.send({act: AdsConst.ACTIONS.AD_STARTED, ads_id: activeAd.id});
});
let adFinished = sc.get(activeAd, 'adFinishedCallback', async () => {
this.isPlayingAd = false;
Logger.info('CrazyGames - Ad-finished callback.', (new Date()).getTime());
this.send({act: AdsConst.ACTIONS.AD_ENDED, ads_id: activeAd.id});
await this.gameManager.audioManager.changeMuteState(false, false);
});
let adError = sc.get(activeAd, 'adErrorCallback', async (error) => {
this.isPlayingAd = false;
Logger.info('CrazyGames - Ad-error callback.', error, (new Date()).getTime());
this.send({act: AdsConst.ACTIONS.AD_ENDED, ads_id: activeAd.id, error});
await this.gameManager.audioManager.changeMuteState(false, false);
});
let rewardItemKey = sc.get(activeAd, 'rewardItemKey', false);
let adType = rewardItemKey ? 'rewarded' : 'midgame';
await this.gameManager.audioManager.changeMuteState(true, true);
await this.sdk.ad.requestAd(adType, {adStarted, adFinished, adError});
}
/**
* @param {Object} props
* @returns {boolean}
*/
send(props)
{
let roomEvents = this.gameManager?.activeRoomEvents;
if(!roomEvents){
Logger.warning('CrazyGames - RoomEvents undefined to send an Ad Video message.');
return false;
}
return roomEvents?.send(props);
}
}
module.exports.VideosHandler = VideosHandler;
================================================
FILE: lib/ads/client/providers/crazy-games.js
================================================
/**
*
* Reldens - CrazyGames
*
* SDK documentation: https://docs.crazygames.com/sdk/html5-v2/#request-banner
*
* Integrates the CrazyGames advertising SDK for banner and video ads.
*
*/
const { BannersHandler } = require('./crazy-games/banners-handler');
const { VideosHandler } = require('./crazy-games/videos-handler');
const { AdsProvider } = require('../ads-provider');
const { AdsConst } = require('../../constants');
const { Logger } = require('@reldens/utils');
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
* @typedef {import('./crazy-games/banners-handler').BannersHandler} BannersHandler
* @typedef {import('./crazy-games/videos-handler').VideosHandler} VideosHandler
*/
class CrazyGames
{
/**
* @param {Object} providerModel
* @param {GameManager} gameManager
*/
constructor(providerModel, gameManager)
{
/** @type {GameManager} */
this.gameManager = gameManager;
/** @type {Object} */
this.gameDom = gameManager?.gameDom;
/** @type {Object} */
this.events = gameManager?.events;
/** @type {Window} */
this.window = gameManager?.gameDom?.getWindow();
/** @type {Object} */
this.metaData = providerModel;
/** @type {Object} */
this.sdk = this.window?.CrazyGames?.SDK;
/** @type {number} */
this.retry = 0;
/** @type {string} */
this.environment = AdsConst.ENVIRONMENTS.DISABLED;
if(!this.metaData.sdkRetryTime){
this.metaData.sdkRetryTime = 500;
}
if(!this.metaData.sdkMaxRetries){
this.metaData.sdkMaxRetries = 10;
}
if(!this.metaData.sdkBannerRefreshTime){
this.metaData.sdkBannerRefreshTime = 60000;
}
/** @type {Object} */
this.activeAds = this.fetchActiveAds(providerModel);
let handlersProps = {
gameManager,
metaData: providerModel,
sdk: this.sdk,
hasAdblock: this.hasAdblock,
isEnabled: this.isEnabled
};
/** @type {BannersHandler} */
this.bannersHandler = new BannersHandler(handlersProps);
/** @type {VideosHandler} */
this.videosHandler = new VideosHandler(handlersProps);
}
/**
* @param {Object} providerModel
* @returns {Object}
*/
fetchActiveAds(providerModel)
{
if(!this.gameManager?.config){
return {};
}
return AdsProvider.fetchActiveAdsByProviderId(
providerModel.id,
this.validAdsTypes(),
this.gameManager.config.get('client/ads/collection', {})
);
}
/**
* @returns {Array}
*/
validAdsTypes()
{
return [AdsConst.ADS_TYPES.BANNER, AdsConst.ADS_TYPES.EVENT_VIDEO];
}
/**
* @returns {Promise}
*/
async activate()
{
if(!this.sdk){
if(this.retry === this.metaData.sdkMaxRetries){
Logger.critical('CrazyGames required object.');
return false;
}
if(this.retry < this.metaData.sdkMaxRetries){
setTimeout(() => {
this.retry++;
Logger.info('CrazyGames required object, retry #'+this.retry+'.');
this.sdk = this.window?.CrazyGames?.SDK;
if(this.sdk){
Logger.info('CrazyGames object found.');
}
this.activate();
}, this.metaData.sdkRetryTime);
}
return false;
}
this.environment = await this.sdk.getEnvironment();
this.bannersHandler.sdk = this.sdk;
this.videosHandler.sdk = this.sdk;
if(await this.hasAdblock()){
return false;
}
await this.activateAds();
}
/**
* @returns {Promise}
*/
async hasAdblock()
{
try {
let result = await this.sdk.ad.hasAdblock();
if(result){
Logger.critical('Adblock detected, please disable.');
}
return result;
} catch (error) {
Logger.info('SDK detected error.', error);
}
return false;
}
/**
* @returns {Promise}
*/
async isEnabled()
{
return AdsConst.ENVIRONMENTS.DISABLED !== await this.sdk.getEnvironment();
}
/**
* @returns {Promise}
*/
async activateAds()
{
let activeKeys = Object.keys(this.activeAds);
if(0 === activeKeys.length){
return false;
}
for(let i of activeKeys){
let activeAd = this.activeAds[i];
if(AdsConst.ADS_TYPES.BANNER === activeAd.type.key){
await this.bannersHandler.activateAdBanner(activeAd);
}
if(AdsConst.ADS_TYPES.EVENT_VIDEO === activeAd.type.key){
await this.videosHandler.activateAdVideo(activeAd);
}
}
}
}
module.exports.CrazyGames = CrazyGames;
================================================
FILE: lib/ads/client/providers/game-monetize.js
================================================
/**
*
* Reldens - GameMonetize
*
* SDK documentation: https://github.com/MonetizeGame/GameMonetize.com-SDK
*
* Integrates the GameMonetize advertising SDK for video ads with reward functionality.
*
*/
const { AdsProvider } = require('../ads-provider');
const { AdsConst } = require('../../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
*/
class GameMonetize
{
/**
* @param {Object} providerModel
* @param {GameManager} gameManager
*/
constructor(providerModel, gameManager)
{
/** @type {GameManager} */
this.gameManager = gameManager;
/** @type {Object} */
this.gameDom = gameManager?.gameDom;
/** @type {Object} */
this.events = gameManager?.events;
/** @type {Window} */
this.window = gameManager?.gameDom?.getWindow();
/** @type {Object} */
this.metaData = providerModel;
this.setSdkOptions();
/** @type {Object} */
this.sdk = this.window?.sdk;
/** @type {number} */
this.retry = 0;
/** @type {boolean} */
this.isPlayingAd = false;
/** @type {string} */
this.environment = AdsConst.ENVIRONMENTS.DISABLED;
if(!this.metaData.sdkRetryTime){
this.metaData.sdkRetryTime = 500;
}
if(!this.metaData.sdkMaxRetries){
this.metaData.sdkMaxRetries = 10;
}
/** @type {Object} */
this.activeAds = this.fetchActiveAds(providerModel);
/** @type {Object|false} */
this.activeAdBeenPlayed = false;
this.setConfig();
}
/**
* @param {Object} providerModel
* @returns {Object}
*/
fetchActiveAds(providerModel)
{
if(!this.gameManager?.config){
return {};
}
return AdsProvider.fetchActiveAdsByProviderId(
providerModel.id,
this.validAdsTypes(),
this.gameManager.config.get('client/ads/collection', {})
);
}
/**
* @returns {Object}
*/
eventKeys()
{
return {
sdkAdStarted: 'CONTENT_PAUSE_REQUESTED',
sdkAdEnded: 'SDK_GAME_START',
sdkReady: 'SDK_READY',
}
}
/**
* @returns {boolean}
*/
setSdkOptions()
{
if(!this.gameDom){
return false;
}
if(!this.metaData.gameId){
Logger.error('GameMonetize - Game ID undefined.');
return false;
}
this.gameDom.getWindow().SDK_OPTIONS = {
gameId: this.metaData.gameId,
onEvent: async (event) => {
Logger.info('GameMonetize - SDK event fired: '+event.name);
switch (event.name) {
case this.eventKeys().sdkAdStarted:
// pause game logic / mute audio:
await this.adStartedCallback(event);
break;
case this.eventKeys().sdkAdEnded:
// advertisement done, resume game logic and unmute audio:
await this.adEndedCallback(event);
break;
case this.eventKeys().sdkReady:
// when sdk is ready:
await this.sdkReadyCallback(event);
break;
}
}
};
}
setConfig()
{
this.videoMinimumDuration = !this.gameManager
? AdsConst.VIDEOS_MINIMUM_DURATION
: this.gameManager.config.getWithoutLogs(
'client/ads/general/providers/gameMonetize/videoMinimumDuration',
AdsConst.VIDEOS_MINIMUM_DURATION
);
this.awaitAdsTime = !this.gameManager
? AdsConst.AWAIT_ADS_TIME
: this.gameManager.config.getWithoutLogs(
'client/ads/general/providers/gameMonetize/awaitAdsTime',
AdsConst.AWAIT_ADS_TIME
);
}
/**
* @returns {Array}
*/
validAdsTypes()
{
return [AdsConst.ADS_TYPES.EVENT_VIDEO];
}
/**
* @param {Object} event
* @returns {Promise}
*/
async adStartedCallback(event)
{
this.isPlayingAd = true;
await this.gameManager.audioManager.changeMuteState(true, true); // mute and lock audio
if(!this.activeAdBeenPlayed){
Logger.info('AdStartedCallback undefined activeAd.', event, this.activeAdBeenPlayed);
return false;
}
Logger.info('GameMonetize - Ad-started callback.', (new Date()).getTime());
this.send({act: AdsConst.ACTIONS.AD_STARTED, ads_id: this.activeAdBeenPlayed.id});
}
/**
* @param {Object} event
* @returns {Promise}
*/
async adEndedCallback(event)
{
this.isPlayingAd = false;
await this.gameManager.audioManager.changeMuteState(false, false);
if(!this.activeAdBeenPlayed){
Logger.info('AdEndedCallback undefined activeAd.', event, this.activeAdBeenPlayed);
return false;
}
Logger.info('GameMonetize - Ad-finished callback.', (new Date()).getTime());
this.send({act: AdsConst.ACTIONS.AD_ENDED, ads_id: this.activeAdBeenPlayed.id});
}
/**
* @param {Object} event
* @returns {Promise}
*/
async sdkReadyCallback(event)
{
this.sdk = this.window.sdk;
}
/**
* @returns {Promise}
*/
async activate()
{
if(!this.sdk){
if(this.retry === this.metaData.sdkMaxRetries){
Logger.critical('GameMonetize required object.');
return false;
}
if(this.retry < this.metaData.sdkMaxRetries){
setTimeout(() => {
this.retry++;
Logger.info('GameMonetize required object, retry #'+this.retry+'.');
this.sdk = this.window?.sdk;
if(this.sdk){
Logger.info('GameMonetize object found.');
}
this.activate();
}, this.metaData.sdkRetryTime);
}
return false;
}
await this.activateAds();
}
/**
* @returns {Promise}
*/
async activateAds()
{
let activeKeys = Object.keys(this.activeAds);
if(0 === activeKeys.length){
Logger.info('None active ads.');
return false;
}
for(let i of activeKeys){
let activeAd = this.activeAds[i];
if(AdsConst.ADS_TYPES.EVENT_VIDEO !== activeAd.type.key){
continue;
}
let eventKey = sc.get(activeAd, 'eventKey', false);
if(!eventKey){
Logger.warning('Missing event key.', activeAd);
return false;
}
this.events.on(eventKey, async (event) => {
Logger.info('GameMonetize - Video event fired, playing ad.', event, activeAd);
if(this.isPlayingAd){
Logger.info('GameMonetize - Ad is been played.');
return false;
}
return await this.tryRePlay(activeAd);
});
}
}
/**
* @param {Object} activeAd
* @returns {Promise}
*/
async tryRePlay(activeAd)
{
let adsPlugin = this.gameManager.getFeature('ads');
if(null === adsPlugin.playedAds){
setTimeout(
() => {
this.tryRePlay(activeAd);
},
this.awaitAdsTime
);
return false;
}
this.activeAdBeenPlayed = activeAd;
if(!sc.isObjectFunction(this.sdk, 'showBanner')){
Logger.critical('GameMonetize SDK not ready.');
return false;
}
await this.sdk.showBanner();
}
/**
* @param {Object} props
* @returns {boolean}
*/
send(props)
{
let roomEvents = this.gameManager?.activeRoomEvents;
if(!roomEvents){
Logger.warning('GameMonetize - RoomEvents undefined to send an Ad Video message.');
return false;
}
return roomEvents.send(props);
}
}
module.exports.GameMonetize = GameMonetize;
================================================
FILE: lib/ads/client/providers/google-ad-sense.js
================================================
/**
*
* Reldens - GoogleAdSense
*
* Documentation: https://support.google.com/adsense/answer/9183549?sjid=16298855257735669764-EU
*
* Placeholder for Google AdSense integration.
*
*/
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
*/
class GoogleAdSense
{
/**
* @param {Object} providerModel
* @param {GameManager} gameManager
*/
constructor(providerModel, gameManager)
{
/** @type {GameManager} */
this.gameManager = gameManager;
/** @type {Object} */
this.gameDom = gameManager?.gameDom;
/** @type {Object} */
this.events = gameManager?.events;
/** @type {Window} */
this.window = gameManager?.gameDom?.getWindow();
/** @type {Object} */
this.metaData = providerModel;
}
}
module.exports.GoogleAdSense = GoogleAdSense;
================================================
FILE: lib/ads/client/providers-list.js
================================================
/**
*
* Reldens - ProvidersList
*
*/
const { CrazyGames } = require('./providers/crazy-games');
const { GameMonetize } = require('./providers/game-monetize');
const { GoogleAdSense } = require('./providers/google-ad-sense');
module.exports.ProvidersList = {
crazyGames: CrazyGames,
gameMonetize: GameMonetize,
googleAdSense: GoogleAdSense
};
================================================
FILE: lib/ads/client/sdk-handler.js
================================================
/**
*
* Reldens - SdkHandler
*
* Handles the initialization and setup of third-party advertising SDKs.
*
*/
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
*
* @typedef {Object} SdkHandlerProps
* @property {Object} [gameDom] - The game DOM wrapper instance
* @property {Object} [config] - Configuration object for the SDK
*/
class SdkHandler
{
/**
* @param {SdkHandlerProps} props
*/
constructor(props)
{
/** @type {Object|false} */
this.gameDom = sc.get(props, 'gameDom', false);
}
/**
* @param {Object} providers
* @param {GameManager} gameManager
* @returns {Promise}
*/
async setupProvidersSdk(providers, gameManager)
{
if(!this.gameDom){
Logger.error('Undefined GameDOM on SdkHandler.');
return false;
}
if(!sc.isObject(providers)){
//Logger.debug('Providers not available.');
return false;
}
let keys = Object.keys(providers);
if(0 === keys.length){
//Logger.debug('Providers not found.');
return false;
}
for(let i of keys){
let provider = providers[i];
await this.appendSdk(provider);
await this.activateSdkInstance(provider, gameManager);
Logger.info('Activated Ads SDK: '+provider.key, provider);
}
}
/**
* @param {Object} provider
* @returns {Promise}
*/
async appendSdk(provider)
{
let sdkUrl = sc.get(provider, 'sdkUrl', '');
if('' === sdkUrl){
//Logger.debug('Provider does not have an SDK URL.', provider);
return false;
}
let body = this.gameDom.getElement('body');
let sdkSource = this.gameDom.createElement('script');
sdkSource.src = sdkUrl;
body.append(sdkSource);
return true;
}
/**
* @param {Object} provider
* @param {GameManager} gameManager
* @returns {Promise}
*/
async activateSdkInstance(provider, gameManager)
{
if(provider.classDefinition){
provider.service = new provider.classDefinition(provider, gameManager, provider.activeAds);
}
if(sc.isFunction(provider.service?.activate)){
await provider.service.activate();
}
}
}
module.exports.SdkHandler = SdkHandler;
================================================
FILE: lib/ads/client/snippets/en_US.js
================================================
/**
*
* Reldens - Translations - en_US
*
*/
module.exports = {
ads: {}
}
================================================
FILE: lib/ads/constants.js
================================================
/**
*
* Reldens - AdsConst
*
*/
module.exports.AdsConst = {
ENVIRONMENTS: {
DISABLED: 'disabled'
},
ADS_TYPES: {
EVENT_VIDEO: 'eventVideo',
BANNER: 'banner'
},
ACTIONS: {
ADS_PLAYED: 'adsP',
AD_STARTED: 'adS',
AD_ENDED: 'adE',
},
MESSAGE: {
DATA_VALUES: {
NAMESPACE: 'ads'
}
},
AWAIT_ADS_TIME: 1000,
VIDEOS_MINIMUM_DURATION: 3000
};
================================================
FILE: lib/ads/server/ads-start-handler.js
================================================
/**
*
* Reldens - AdsStartHandler
*
* Initializes and loads ad configuration data from the database for client and server use.
*
*/
const { BaseAd } = require('./ads-type/base-ad');
const { Banner } = require('./ads-type/banner');
const { EventVideo } = require('./ads-type/event-video');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
*
* @typedef {Object} AdsStartHandlerProps
* @property {EventsManager} [events] - The events manager instance
* @property {BaseDataServer} [dataServer] - The data server instance
* @property {Object} [configProcessor] - The configuration processor instance
*/
class AdsStartHandler
{
/**
* @param {AdsStartHandlerProps} props
* @returns {Promise}
*/
async initialize(props)
{
/** @type {EventsManager|false} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in AdsStartHandler.');
}
/** @type {BaseDataServer|false} */
this.dataServer = sc.get(props, 'dataServer', false);
if(!this.dataServer){
Logger.error('DataServer undefined in AdsStartHandler.');
}
/** @type {Object|false} */
this.configProcessor = sc.get(props, 'configProcessor', false);
if(!this.configProcessor){
Logger.error('configProcessor undefined in AdsStartHandler.');
}
await this.loadData();
await this.enrichAds();
}
/**
* @returns {Promise}
*/
async loadData()
{
sc.deepMergeProperties(this.configProcessor, {
configList: {
client: {
ads: {
general: {
types: await this.mapTypes(),
providers: await this.mapProviders(),
},
collection: {}
}
},
server: {
ads: {
modelsCollection: await this.dataServer.getEntity('ads').loadAllWithRelations([
'related_ads_providers',
'related_ads_types',
'related_ads_event_video',
'related_ads_banner'
]) || [],
collection: {}
}
}
}
});
}
/**
* @returns {Promise>}
*/
async mapProviders()
{
let providersModels = await this.dataServer.getEntity('adsProviders').loadAll() || [];
if(!providersModels || 0 === providersModels.length){
return {};
}
let providers = {};
for(let provider of providersModels){
providers[provider.key] = provider;
}
return providers;
}
/**
* @returns {Promise>}
*/
async mapTypes()
{
let typesModels = await this.dataServer.getEntity('adsTypes').loadAll() || [];
if(!typesModels || 0 === typesModels.length){
return {};
}
let adTypes = {};
for(let adType of typesModels){
adTypes[adType.key] = adType;
}
return adTypes;
}
/**
* @returns {Promise}
*/
async enrichAds()
{
for(let ad of this.configProcessor.configList.server.ads.modelsCollection){
let adInstance = this.instanceByType(ad);
this.configProcessor.configList.server.ads.collection[ad.id] = adInstance;
this.configProcessor.configList.client.ads.collection[ad.id] = adInstance.clientData();
}
}
/**
* @param {Object} ad
* @returns {BaseAd|Banner|EventVideo}
*/
instanceByType(ad)
{
if(ad.related_ads_event_video){
return EventVideo.fromModel(ad);
}
if(ad.related_ads_banner){
return Banner.fromModel(ad);
}
return BaseAd.fromModel(ad);
}
}
module.exports.AdsStartHandler = AdsStartHandler;
================================================
FILE: lib/ads/server/ads-type/banner.js
================================================
/**
*
* Reldens - Banner
*
* Banner ad type with full-time and UI-triggered display modes.
*
* - FullTimeBanner:
* This banner will be displayed from the login page.
* If it is closed, the game will reload the page.
* It will be re-generated every X time.
*
* - OpenUiBanner:
* This banner will be displayed every time the specified UI (all or a single one) is opened and automatically closed
* when the UI is closed.
*
*/
const { BaseAd } = require('./base-ad');
const { Logger, sc } = require('@reldens/utils');
class Banner extends BaseAd
{
/**
* @param {Object} adsModel
* @returns {Banner}
*/
static fromModel(adsModel)
{
return new this(adsModel);
}
/**
* @param {Object} adsModel
*/
constructor(adsModel)
{
super(adsModel);
return this.setBannerDataFromModel(adsModel);
}
/**
* @param {Object} adsModel
* @returns {boolean}
*/
setBannerDataFromModel(adsModel)
{
let adsBanner = sc.get(adsModel, 'related_ads_banner');
if(!adsBanner){
Logger.warning('Parent banner not provided on AdsModel for Banner.', adsModel);
return false;
}
this.bannerData = sc.parseJson(adsBanner.banner_data);
// @TODO - BETA - Add item rewards on banner click X times validated on server side.
}
/**
* @returns {Object}
*/
clientData()
{
let data = super.clientData();
data.bannerData = this.bannerData;
return data;
}
}
module.exports.Banner = Banner;
================================================
FILE: lib/ads/server/ads-type/base-ad.js
================================================
/**
*
* Reldens - BaseAd
*
* Base class for all ad types containing common properties and client data generation.
*
*/
const { Logger } = require('@reldens/utils');
class BaseAd
{
/**
* @param {Object} adsModel
* @returns {BaseAd}
*/
static fromModel(adsModel)
{
return new this(adsModel);
}
/**
* @param {Object} adsModel
*/
constructor(adsModel)
{
this.setData(adsModel);
}
/**
* @param {Object} adsModel
* @returns {boolean}
*/
setData(adsModel)
{
if(!adsModel){
Logger.warning('AdsModel not provided on BaseAd.');
return false;
}
Object.assign(this, {
id: adsModel.id,
key: adsModel.key,
providerId: adsModel.provider_id,
typeId: adsModel.type_id,
width: adsModel.width,
height: adsModel.height,
position: adsModel.position,
top: adsModel.top,
bottom: adsModel.bottom,
left: adsModel.left,
right: adsModel.right,
enabled: adsModel.enabled,
replay: Boolean(adsModel.replay),
provider: adsModel?.related_ads_providers || null,
type: adsModel?.related_ads_types || null
});
}
/**
* @returns {Object}
*/
clientData()
{
return {
id: this.id,
key: this.key,
type: {
id: this.typeId,
key: this.type?.key
},
provider: {
id: this.providerId,
key: this.provider?.key
},
styles: {
width: this.width,
height: this.height,
position: this.position,
top: this.top,
bottom: this.bottom,
left: this.left,
right: this.right,
},
enabled: this.enabled,
replay: this.replay
};
}
}
module.exports.BaseAd = BaseAd;
================================================
FILE: lib/ads/server/ads-type/event-video.js
================================================
/**
*
* Reldens - EventVideo
*
* Video ad type triggered by game events with an optional reward system.
*
* This video will be visible every time the specified "reldens" event is fired (experimental).
*
* - ItemVideoReward:
* This video will be visible as per request when a specific item is available on an NPC.
* After seeing the ad, you will get the specified item as a reward.
*
* - SceneChangeVideo:
* This video will be visible every time you enter on the specified scene.
*
*/
const { BaseAd } = require('./base-ad');
const { Logger, sc } = require('@reldens/utils');
class EventVideo extends BaseAd
{
/**
* @param {Object} adsModel
* @returns {EventVideo}
*/
static fromModel(adsModel)
{
return new this(adsModel);
}
/**
* @param {Object} adsModel
*/
constructor(adsModel)
{
super(adsModel);
return this.setVideoDataFromModel(adsModel);
}
/**
* @param {Object} adsModel
* @returns {boolean}
*/
setVideoDataFromModel(adsModel)
{
let adsVideo = sc.get(adsModel, 'related_ads_event_video');
if(!adsVideo){
Logger.warning('Parent video not provided on AdsModel for EventVideo.', adsModel);
return false;
}
this.eventKey = adsVideo.event_key;
this.eventData = sc.parseJson(adsVideo.event_data);
this.rewardItemKey = sc.get(this.eventData, 'rewardItemKey');
this.rewardItemQty = sc.get(this.eventData, 'rewardItemQty');
}
/**
* @returns {Object}
*/
clientData()
{
let data = super.clientData();
data.eventKey = this.eventKey;
return data;
}
}
module.exports.EventVideo = EventVideo;
================================================
FILE: lib/ads/server/entities/ads-entity-override.js
================================================
/**
*
* Reldens - AdsEntityOverride
*
* Customizes the ads entity properties for the admin panel display.
*
*/
const { AdsEntity } = require('../../../../generated-entities/entities/ads-entity');
const { sc } = require('@reldens/utils');
class AdsEntityOverride extends AdsEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.listProperties = sc.removeFromArray(config.listProperties, [
'top',
'bottom',
'left',
'right'
]);
config.navigationPosition = 1200;
return config;
}
}
module.exports.AdsEntityOverride = AdsEntityOverride;
================================================
FILE: lib/ads/server/entities-config.js
================================================
/**
*
* Reldens - Entities Config
*
*/
const { AdsEntityOverride} = require('./entities/ads-entity-override');
module.exports.entitiesConfig = {
ads: AdsEntityOverride,
};
================================================
FILE: lib/ads/server/entities-translations.js
================================================
/**
*
* Reldens - Entities Translations
*
*/
module.exports.entitiesTranslations = {
labels: {
ads: 'Ads',
ads_types: 'Types',
ads_providers: 'Providers',
ads_banner: 'Banners',
ads_event_video: 'Videos',
ads_played: 'Played'
}
};
================================================
FILE: lib/ads/server/event-handlers/create-player-ads-handler.js
================================================
/**
*
* Reldens - CreatePlayerAdsHandler
*
* Handles player creation events to load and send played ads data to the client.
*
*/
const { AdsConst } = require('../../constants');
const { Logger } = require('@reldens/utils');
/**
* @typedef {import('../plugin').AdsPlugin} AdsPlugin
*/
class CreatePlayerAdsHandler
{
/**
* @param {AdsPlugin} adsPlugin
*/
constructor(adsPlugin)
{
/** @type {Object} */
this.adsPlayedRepository = adsPlugin.dataServer.getEntity('adsPlayed');
/** @type {Object>} */
this.adsByPlayerId = {};
}
/**
* @param {Object} playerSchema
* @param {Object} client
* @returns {Promise}
*/
async enrichPlayedWithPlayedAds(playerSchema, client)
{
if(!this.adsPlayedRepository){
Logger.error('Missing adsPlayedRepository in "CreatePlayerAdsHandler".');
return false;
}
if(!this.adsByPlayerId[playerSchema.player_id]){
this.adsByPlayerId[playerSchema.player_id] = await this.adsPlayedRepository.loadByWithRelations(
'player_id',
playerSchema.player_id,
['related_players']
);
}
if(!this.adsByPlayerId[playerSchema.player_id] || 0 === this.adsByPlayerId[playerSchema.player_id].length){
return false;
}
playerSchema.setCustom('playedAds', this.adsByPlayerId[playerSchema.player_id]);
await client.send('*', {
act: AdsConst.ACTIONS.ADS_PLAYED,
playedAdsModels: this.adsByPlayerId[playerSchema.player_id]
});
}
}
module.exports.CreatePlayerAdsHandler = CreatePlayerAdsHandler;
================================================
FILE: lib/ads/server/message-actions.js
================================================
/**
*
* Reldens - AdsMessageActions
*
* Handles ad-related message actions including ad start/end events and reward distribution.
*
*/
const { GiveRewardAction } = require('../../rewards/server/actions/give-reward-action');
const { AdsConst } = require('../constants');
const { GameConst } = require('../../game/constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
* @typedef {import('./plugin').AdsPlugin} AdsPlugin
* @typedef {import('../../rewards/server/actions/give-reward-action').GiveRewardAction} GiveRewardAction
*
* @typedef {Object} AdsMessageActionsProps
* @property {BaseDataServer} [dataServer] - The data server instance
* @property {AdsPlugin} [adsPlugin] - The ads plugin instance
*/
class AdsMessageActions
{
/**
* @param {AdsMessageActionsProps} props
*/
constructor(props)
{
/** @type {BaseDataServer|false} */
this.dataServer = sc.get(props, 'dataServer', false);
if(!this.dataServer){
Logger.error('DataServer undefined in AdsMessageActions.');
}
/** @type {AdsPlugin|false} */
this.adsPlugin = sc.get(props, 'adsPlugin', false);
if(!this.dataServer){
Logger.error('AdsPlugin undefined in AdsMessageActions.');
}
/** @type {GiveRewardAction} */
this.giveRewardAction = new GiveRewardAction();
this.setRepository();
}
/**
* @returns {boolean}
*/
setRepository()
{
if(!this.dataServer){
return false;
}
/** @type {Object} */
this.adsPlayedRepository = this.dataServer.getEntity('adsPlayed');
}
/**
* @param {number} playerId
* @param {number} adId
* @returns {Promise}
*/
async loadPlayedAd(playerId, adId)
{
if(!this.adsPlayedRepository){
return false;
}
return await this.adsPlayedRepository.loadOne({player_id: playerId, ads_id: adId});
}
/**
* @param {number} playerId
* @param {number} adId
* @param {string|null} [startedAt]
* @param {string|null} [endedAt]
* @returns {Promise}
*/
async upsertPlayedAd(playerId, adId, startedAt = null, endedAt = null)
{
let newAdData = {
player_id: playerId,
ads_id: adId
};
if(null !== startedAt){
newAdData['started_at'] = startedAt;
}
if(null !== endedAt){
newAdData['ended_at'] = endedAt;
}
try {
let playedAdModel = await this.loadPlayedAd(playerId, adId);
if(!playedAdModel){
return this.adsPlayedRepository.create(newAdData);
}
return this.adsPlayedRepository.updateById(playedAdModel.id, newAdData);
} catch (error) {
Logger.error(error.message);
return false;
}
}
/**
* @param {Object} client
* @param {Object} data
* @param {Object} room
* @param {Object} playerSchema
* @returns {Promise}
*/
async executeMessageActions(client, data, room, playerSchema)
{
if(!this.dataServer || !this.adsPlugin){
return false;
}
await this.adStart(data, room, playerSchema);
await this.adEnded(data, room, playerSchema);
return {client, data, room, playerSchema};
}
/**
* @param {Object} data
* @param {Object} room
* @param {Object} playerSchema
* @returns {Promise}
*/
async adStart(data, room, playerSchema)
{
if(data.act !== AdsConst.ACTIONS.AD_STARTED){
return false;
}
room.deactivatePlayer(playerSchema, room, GameConst.STATUS.DISABLED);
let saveResult = await this.upsertPlayedAd(playerSchema.player_id, data.ads_id, sc.getCurrentDate(), null);
if(!saveResult){
Logger.critical('Ad started save error.', data, playerSchema.player_id);
}
return saveResult;
}
/**
* @param {Object} data
* @param {Object} room
* @param {Object} playerSchema
* @returns {Promise}
*/
async adEnded(data, room, playerSchema)
{
if(data.act !== AdsConst.ACTIONS.AD_ENDED){
return false;
}
room.activatePlayer(playerSchema, GameConst.STATUS.ACTIVE);
let saveResult = await this.upsertPlayedAd(playerSchema.player_id, data.ads_id, null, sc.getCurrentDate());
if(!saveResult){
Logger.critical('Ad ended save error.', data, playerSchema.player_id);
}
let playedAdModel = await this.loadPlayedAd(playerSchema.player_id, data.ads_id);
let playedAd = sc.get(room.config.get('server/ads/collection'), data.ads_id, false);
if(!playedAd.rewardItemKey){
Logger.info('Reward item not specified.');
return false;
}
let playedTime = (new Date(playedAdModel.ended_at)).getTime() - (new Date(playedAdModel.startedAt)).getTime();
let minimumDuration = room.config.getWithoutLogs(
'client/ads/general/providers/'+playedAd.provider.key+'/videoMinimumDuration',
AdsConst.VIDEOS_MINIMUM_DURATION
);
if(playedTime < minimumDuration){
Logger.info('Invalid reward duration.', playerSchema.player_id, playedAd.id);
setTimeout(async () => {
return await this.giveRewardItem(playerSchema, playedAd);
}, (minimumDuration - playedTime));
return true;
}
return await this.giveRewardItem(playerSchema, playedAd);
}
/**
* @param {Object} playerSchema
* @param {Object} playedAd
* @returns {Promise}
*/
async giveRewardItem(playerSchema, playedAd)
{
return this.giveRewardAction.execute(playerSchema, playedAd.rewardItemKey, playedAd.rewardItemQty);
}
}
module.exports.AdsMessageActions = AdsMessageActions;
================================================
FILE: lib/ads/server/plugin.js
================================================
/**
*
* Reldens - Ads Server Plugin
*
* Server-side ads plugin that manages ad configuration, player ad tracking, and reward distribution.
*
*/
const { AdsStartHandler } = require('./ads-start-handler');
const { AdsMessageActions } = require('./message-actions');
const { CreatePlayerAdsHandler } = require('./event-handlers/create-player-ads-handler');
const { PluginInterface } = require('../../features/plugin-interface');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
* @typedef {import('./ads-start-handler').AdsStartHandler} AdsStartHandler
* @typedef {import('./event-handlers/create-player-ads-handler').CreatePlayerAdsHandler} CreatePlayerAdsHandler
*
* @typedef {Object} AdsPluginProps
* @property {EventsManager} [events] - The events manager instance
* @property {BaseDataServer} [dataServer] - The data server instance
*/
class AdsPlugin extends PluginInterface
{
/**
* @param {AdsPluginProps} props
*/
setup(props)
{
/** @type {EventsManager|false} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in AdsPlugin.');
}
/** @type {BaseDataServer|false} */
this.dataServer = sc.get(props, 'dataServer', false);
if(!this.dataServer){
Logger.error('DataServer undefined in AdsPlugin.');
}
/** @type {AdsStartHandler} */
this.adsStartHandler = new AdsStartHandler();
/** @type {CreatePlayerAdsHandler} */
this.createPlayerAdsHandler = new CreatePlayerAdsHandler(this);
this.listenEvents();
}
/**
* @returns {boolean}
*/
listenEvents()
{
if(!this.events || !this.dataServer){
return false;
}
this.events.on('reldens.serverConfigFeaturesReady', async (props) => {
await this.adsStartHandler.initialize({
events: this.events,
dataServer: this.dataServer,
configProcessor: props.configProcessor
});
});
this.events.on('reldens.roomsMessageActionsGlobal', (roomMessageActions) => {
roomMessageActions.ads = new AdsMessageActions({dataServer: this.dataServer, adsPlugin: this});
});
this.events.on('reldens.createPlayerAfter', async (client, userModel, playerSchema) => {
await this.createPlayerAdsHandler.enrichPlayedWithPlayedAds(playerSchema, client);
});
}
}
module.exports.AdsPlugin = AdsPlugin;
================================================
FILE: lib/audio/client/audio-ui.js
================================================
/**
*
* Reldens - AudioUi
*
* Manages the audio settings user interface in the game.
*
*/
const { SceneAudioPlayer } = require('./scene-audio-player');
const { AudioUpdate } = require('./audio-update');
const { sc } = require('@reldens/utils');
/**
* @typedef {import('phaser').Scene} PhaserScene
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
* @typedef {import('./manager').AudioManager} AudioManager
* @typedef {import('./scene-audio-player').SceneAudioPlayer} SceneAudioPlayer
*/
class AudioUi
{
/**
* @param {PhaserScene} uiScene
*/
constructor(uiScene)
{
/** @type {PhaserScene} */
this.uiScene = uiScene;
/** @type {GameManager} */
this.gameManager = this.uiScene.gameManager;
/** @type {AudioManager} */
this.audioManager = this.gameManager.audioManager;
/** @type {SceneAudioPlayer} */
this.sceneAudioPlayer = SceneAudioPlayer;
}
/**
* @returns {boolean|void}
*/
createUi()
{
if(!this.audioManager.categories){
return;
}
let audioSettingsTemplate = this.uiScene.cache.html.get('audio');
let audioCategoryTemplate = this.uiScene.cache.html.get('audio-category');
let audioSettingsContent = this.prepareAudioSettingsContent(audioCategoryTemplate, audioSettingsTemplate);
this.gameManager.gameDom.appendToElement('#settings-dynamic', audioSettingsContent);
let audioSettingInputs = this.gameManager.gameDom.getElements('.audio-setting');
if(0 === audioSettingInputs.length){
return false;
}
for(let settingInput of audioSettingInputs){
settingInput.addEventListener('click', async (event) => {
await this.audioManager.setAudio(event.target.dataset.categoryKey, settingInput.checked);
this.gameManager.activeRoomEvents.send(new AudioUpdate(settingInput.value, settingInput.checked));
this.sceneAudioPlayer.playSceneAudio(this.audioManager, this.gameManager.getActiveScene());
});
}
}
/**
* @param {string} audioCategoryTemplate
* @param {string} audioSettingsTemplate
* @returns {string}
*/
prepareAudioSettingsContent(audioCategoryTemplate, audioSettingsTemplate)
{
let categoriesRows = this.prepareCategoriesRows(audioCategoryTemplate);
return this.gameManager.gameEngine.parseTemplate(
audioSettingsTemplate,
{
audioCategories: categoriesRows,
settingsTitle: this.gameManager.services.translator.t('audio.settingsTitle')
}
);
}
/**
* @param {string} audioCategoryTemplate
* @returns {string}
*/
prepareCategoriesRows(audioCategoryTemplate)
{
let categoriesRows = '';
let audioCategoriesKeys = Object.keys(this.audioManager.categories);
for(let i of audioCategoriesKeys){
let audioCategory = this.audioManager.categories[i];
let audioEnabled = sc.get(this.audioManager.playerConfig, audioCategory.id, audioCategory.enabled);
categoriesRows = categoriesRows + this.gameManager.gameEngine.parseTemplate(audioCategoryTemplate, {
categoryId: audioCategory.id,
categoryLabel: audioCategory.category_label,
categoryKey: audioCategory.category_key,
categoryChecked: audioEnabled ? ' checked="checked"' : ''
});
}
return categoriesRows;
}
}
module.exports.AudioUi = AudioUi;
================================================
FILE: lib/audio/client/audio-update.js
================================================
/**
*
* Reldens - AudioUpdate
*
* Represents an audio update message to be sent to the server.
*
*/
const { AudioConst } = require('../constants');
const { GameConst } = require('../../game/constants');
class AudioUpdate
{
/**
* @param {string} updateType - The audio category key to update
* @param {boolean} updateValue - Whether the audio category should be enabled or disabled
*/
constructor(updateType, updateValue)
{
this[GameConst.ACTION_KEY] = AudioConst.AUDIO_UPDATE;
this[AudioConst.MESSAGE.DATA.UPDATE_TYPE] = updateType;
this[AudioConst.MESSAGE.DATA.UPDATE_VALUE] = updateValue;
}
}
module.exports.AudioUpdate = AudioUpdate;
================================================
FILE: lib/audio/client/manager.js
================================================
/**
*
* Reldens - Audio Manager
*
* Manages audio playback, categories, and player audio configuration on the client.
*
*/
const { AudioConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManagerSingleton} EventsManagerSingleton
* @typedef {import('phaser').Scene} PhaserScene
*/
/**
* @typedef {Object} AudioManagerProps
* @property {EventsManagerSingleton} [events]
* @property {Object} [globalAudios]
* @property {Object} [roomsAudios]
* @property {Object} [categories]
* @property {Object} [playerConfig]
* @property {Object} [currentPlayerData]
*/
class AudioManager
{
/**
* @param {AudioManagerProps} props
*/
constructor(props)
{
/** @type {EventsManagerSingleton|boolean} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in AudioManager.');
}
/** @type {Object} */
this.globalAudios = sc.get(props, 'globalAudios', {});
/** @type {Object} */
this.roomsAudios = sc.get(props, 'roomsAudios', {});
/** @type {Object} */
this.categories = sc.get(props, 'categories', {});
/** @type {Object} */
this.playerConfig = sc.get(props, 'playerConfig', {});
/** @type {Object} */
this.currentPlayerData = sc.get(props, 'currentPlayerData', {});
/** @type {Object} */
this.playing = {};
/** @type {boolean} */
this.currentMuteState = false;
/** @type {Object} */
this.changedMutedState = {};
/** @type {boolean} */
this.lockedMuteState = false;
/** @type {Object} */
this.defaultAudioConfig = {
mute: false,
volume: 1,
rate: 1,
detune: 0,
seek: 0,
loop: true,
delay: 0
};
}
/**
* @param {string} audioKey
* @param {boolean} enabled
* @returns {Promise}
*/
async setAudio(audioKey, enabled)
{
if(this.lockedMuteState){
Logger.info('Locked mute state to set audio.');
return false;
}
await this.events.emit('reldens.setAudio', {
audioManager: this,
categoryKey: audioKey,
enabled
});
let category = this.categories[audioKey];
this.playerConfig[category.id] = enabled ? 1 : 0;
if(!sc.hasOwn(this.playing, audioKey)){
return true;
}
let playOrStop = enabled ? 'play' : 'stop';
let playingElement = this.playing[audioKey];
if(category.single_audio && sc.isObjectFunction(playingElement, playOrStop)){
// if is single track we will stop or play the last audio:
return this.setAudioForSingleEntity(playingElement, playOrStop, audioKey, enabled);
}
return this.setAudioForElementChildren(playingElement, category, enabled);
}
/**
* @param {Object} playingElement
* @param {string} playOrStop
* @param {string} audioKey
* @param {boolean} enabled
* @returns {boolean}
*/
setAudioForSingleEntity(playingElement, playOrStop, audioKey, enabled)
{
if(!playingElement){
Logger.error('Missing playingElement.', {audioKey, playingElement});
return false;
}
if(!playingElement.currentConfig){
// Logger.error('Possible null WebAudioSound as playingElement.', {audioKey, playingElement});
return false;
}
if(!sc.isObjectFunction(playingElement, playOrStop)){
Logger.error('Missing playOrStop method in playingElement.', {audioKey, playOrStop, playingElement});
return false;
}
try {
playingElement[playOrStop]();
playingElement.mute = !enabled;
} catch (error) {
Logger.error('PlayingElement error.', {audioKey, playOrStop, playingElement, error});
}
return true;
}
/**
* @param {Object} playingElement
* @param {Object} category
* @param {boolean} enabled
* @returns {boolean}
*/
setAudioForElementChildren(playingElement, category, enabled)
{
// if is multi-track, we will only stop all the audios but replay them only when the events require it:
if(category.single_audio){
return false;
}
let audioKeys = Object.keys(playingElement);
if(0 === audioKeys.length){
return false;
}
for(let i of audioKeys){
this.setAudioForSingleEntity(playingElement[i], 'stop', i, enabled);
}
return true;
}
/**
* @param {PhaserScene} onScene
* @param {Object} audio
* @returns {Object|boolean}
*/
generateAudio(onScene, audio)
{
let soundConfig = Object.assign({}, this.defaultAudioConfig, (audio.config || {}));
if(!sc.hasOwn(onScene.cache.audio.entries.entries, audio.audio_key)){
Logger.error('Audio file does not exists. Key: '+audio.audio_key, onScene.cache.audio.entries.entries);
return false;
}
let audioInstance = onScene.sound.add(audio.audio_key, soundConfig);
//Logger.debug('Generate audio:', audio, soundConfig);
if(audio.related_audio_markers && 0 < audio.related_audio_markers.length){
for(let marker of audio.related_audio_markers){
let markerConfig = Object.assign({}, soundConfig, (marker.config || {}), {
name: marker.marker_key,
start: marker.start,
duration: marker.duration,
});
audioInstance.addMarker(markerConfig);
}
}
return {data: audio, audioInstance};
}
/**
* @param {string} audioKey
* @param {string} sceneKey
* @returns {Object|boolean}
*/
findAudio(audioKey, sceneKey)
{
return this.findRoomAudio(audioKey, sceneKey) || this.findGlobalAudio(audioKey);
}
/**
* @param {string} audioKey
* @param {string} sceneKey
* @returns {Object|boolean}
*/
findRoomAudio(audioKey, sceneKey)
{
if(!sc.hasOwn(this.roomsAudios, sceneKey)){
this.roomsAudios[sceneKey] = {};
}
return this.findAudioInObjectKey(audioKey, this.roomsAudios[sceneKey]);
}
/**
* @param {string} audioKey
* @returns {Object|boolean}
*/
findGlobalAudio(audioKey)
{
return this.findAudioInObjectKey(audioKey, this.globalAudios);
}
/**
* @param {string} audioKey
* @param {Object} audiosObject
* @returns {Object|boolean}
*/
findAudioInObjectKey(audioKey, audiosObject)
{
if(sc.hasOwn(audiosObject, audioKey)){
return {audio: audiosObject[audioKey], marker: false};
}
let objectKeys = Object.keys(audiosObject);
if(0 === objectKeys.length){
return false;
}
for(let i of objectKeys){
let audio = audiosObject[i];
if(sc.hasOwn(audio.audioInstance.markers, audioKey)){
return {audio, marker: audioKey};
}
}
return false;
}
/**
* @param {Array} categories
*/
addCategories(categories)
{
for(let category of categories){
if(!sc.hasOwn(this.categories, category.category_key)){
this.categories[category.category_key] = category;
}
if(!sc.hasOwn(this.playing, category.category_key)){
this.playing[category.category_key] = {};
}
}
}
/**
* @param {Object} audios
* @param {PhaserScene} currentScene
* @returns {Promise}
*/
async loadGlobalAudios(audios, currentScene)
{
let audioKeys = Object.keys(audios);
if(0 === audioKeys.length){
return false;
}
await this.loadByKeys(audioKeys, audios, currentScene, 'globalAudios');
}
/**
* @param {Object} audios
* @param {PhaserScene} currentScene
* @returns {Promise}
*/
async loadAudiosInScene(audios, currentScene)
{
let audioKeys = Object.keys(audios);
if(0 === audioKeys.length){
return false;
}
if(!sc.hasOwn(this.roomsAudios, currentScene.key)){
this.roomsAudios[currentScene.key] = {};
}
await this.loadByKeys(audioKeys, audios, currentScene, 'roomsAudios');
}
/**
* @param {Array} audioKeys
* @param {Object} audios
* @param {PhaserScene} currentScene
* @param {string} storageKey
* @returns {Promise}
*/
async loadByKeys(audioKeys, audios, currentScene, storageKey)
{
let newAudiosCounter = 0;
for(let i of audioKeys){
let audio = audios[i];
this.removeSceneAudioByAudioKey(currentScene, audio.audio_key);
let filesArr = await this.prepareFiles(audio);
if(0 === filesArr.length){
continue;
}
let audioLoader = currentScene.load.audio(audio.audio_key, filesArr);
audioLoader.on('filecomplete', async (completedFileKey) => {
if(completedFileKey !== audio.audio_key){
return false;
}
let generateAudio = this.generateAudio(currentScene, audio);
if(false === generateAudio){
Logger.error('AudioLoader can not generate the audio.', {
'Audio key:': audio.audio_key,
'Storage key:': storageKey,
});
return false;
}
storageKey === 'roomsAudios'
? this.roomsAudios[currentScene.key][audio.audio_key] = generateAudio
: this.globalAudios[audio.audio_key] = generateAudio;
newAudiosCounter++;
await this.fireAudioEvents(audios, currentScene, audio, newAudiosCounter);
});
audioLoader.start();
}
}
/**
* @param {string} url
* @returns {Promise}
*/
async existsFileByXMLHttpRequest(url)
{
try {
let response = await fetch(url, { method: 'HEAD' });
return response.status !== 404;
} catch (error) {
Logger.error('Error fetching:', error);
return false;
}
}
/**
* @param {Object} audio
* @returns {Promise>}
*/
async prepareFiles(audio)
{
let filesName = audio.files_name.split(',');
let filesArr = [];
for(let fileName of filesName){
let audioPath = AudioConst.AUDIO_BUCKET + '/' + fileName;
let testPath = await this.existsFileByXMLHttpRequest(audioPath);
if(false === testPath){
continue;
}
filesArr.push(audioPath);
}
return filesArr;
}
/**
* @param {Object} audios
* @param {PhaserScene} currentScene
* @param {Object} audio
* @param {number} newAudiosCounter
* @returns {Promise}
*/
async fireAudioEvents(audios, currentScene, audio, newAudiosCounter)
{
await currentScene.gameManager.events.emit('reldens.audioLoaded', this, audios, currentScene, audio);
if(newAudiosCounter === audios.length){
await currentScene.gameManager.events.emit('reldens.allAudiosLoaded', this, audios, currentScene, audio);
}
}
/**
* @param {Array} audios
* @param {PhaserScene} currentScene
* @returns {boolean}
*/
removeAudiosFromScene(audios, currentScene)
{
if(0 === audios.length || !currentScene){
return false;
}
for(let audio of audios){
this.removeSceneAudioByAudioKey(currentScene, audio.audio_key);
}
return true;
}
/**
* @param {PhaserScene} scene
* @param {string} audioKey
*/
removeSceneAudioByAudioKey(scene, audioKey)
{
scene.sound.removeByKey(audioKey);
if(sc.hasOwn(scene.cache.audio.entries.entries, audioKey)){
delete scene.cache.audio.entries.entries[audioKey];
}
if(sc.hasOwn(this.roomsAudios[scene.key], audioKey)){
delete this.roomsAudios[scene.key][audioKey];
}
if(sc.hasOwn(this.globalAudios, audioKey)){
delete this.globalAudios[audioKey];
}
}
/**
* @param {Object} defaultAudioConfig
*/
updateDefaultConfig(defaultAudioConfig)
{
if(sc.isObject(defaultAudioConfig)){
Object.assign(this.defaultAudioConfig, defaultAudioConfig);
}
}
/**
* @param {Object} message
* @param {Object} room
* @param {Object} gameManager
* @returns {Promise}
*/
async processUpdateData(message, room, gameManager)
{
if(message.playerConfig){
this.playerConfig = message.playerConfig;
}
if(message.categories){
this.addCategories(message.categories);
await this.events.emit('reldens.audioManagerUpdateCategoriesLoaded', this, room, gameManager, message);
}
let audios = sc.get(message, 'audios', {});
if(0 < Object.keys(audios).length){
let currentScene = gameManager.gameEngine.scene.getScene(room.name);
await this.loadAudiosInScene(audios, currentScene);
await this.events.emit('reldens.audioManagerUpdateAudiosLoaded', this, room, gameManager, message);
}
}
/**
* @param {Object} message
* @param {Object} room
* @param {Object} gameManager
* @returns {Promise}
*/
async processDeleteData(message, room, gameManager)
{
if(0 === message.audios.length){
return false;
}
let currentScene = gameManager.gameEngine.scene.getScene(room.name);
this.removeAudiosFromScene(message.audios, currentScene);
await this.events.emit('reldens.audioManagerDeleteAudios', this, room, gameManager, message);
}
/**
* @returns {boolean}
*/
destroySceneAudios()
{
let playingKeys = Object.keys(this.playing);
if(0 === playingKeys.length){
return false;
}
for(let i of playingKeys){
let playingAudioCategory = this.playing[i];
let categoryData = this.categories[i];
// @TODO - BETA - Check and refactor if possible to use scene delete by key.
if(categoryData.single_audio && sc.isObjectFunction(playingAudioCategory, 'stop')){
playingAudioCategory.stop();
delete this.playing[i];
continue;
}
let playingCategoryKeys = Object.keys(playingAudioCategory);
if(!categoryData.single_audio && 0 === playingCategoryKeys.length){
continue;
}
for(let a of playingCategoryKeys){
let playingAudio = playingAudioCategory[a];
if(sc.isObjectFunction(playingAudio, 'stop')){
playingAudio.stop();
delete playingAudio[i];
}
}
}
}
/**
* @param {boolean} newMuteState
* @param {boolean} newMuteLockState
* @returns {Promise}
*/
async changeMuteState(newMuteState, newMuteLockState)
{
if(false === newMuteLockState){
this.lockedMuteState = false;
}
this.currentMuteState = newMuteState;
if(this.lockedMuteState && false !== newMuteLockState){
Logger.info('Locked mute state from changes.');
return false;
}
return newMuteState ? await this.muteCategories(newMuteLockState) : await this.restoreMute(newMuteLockState);
}
/**
* @param {boolean} newMuteLockState
* @returns {Promise}
*/
async muteCategories(newMuteLockState)
{
let categoriesKeys = Object.keys(this.categories);
if(0 < categoriesKeys.length){
Logger.info('Mute categories not found.');
return false;
}
for(let i of categoriesKeys){
this.changedMutedState[i] = this.currentMuteState;
await this.setAudio(i, false);
}
this.setMuteLock(newMuteLockState);
return true;
}
/**
* @param {boolean} newMuteLockState
* @returns {Promise}
*/
async restoreMute(newMuteLockState)
{
let changedKeys = Object.keys(this.changedMutedState);
if(0 === changedKeys.length){
this.setMuteLock(newMuteLockState);
return false;
}
for(let i of changedKeys){
await this.setAudio(i, true);
}
this.setMuteLock(newMuteLockState);
this.changedMutedState = {};
return true;
}
/**
* @param {boolean} newMuteLockState
*/
setMuteLock(newMuteLockState)
{
if(false === newMuteLockState){
this.lockedMuteState = false;
}
if(true === newMuteLockState){
this.lockedMuteState = true;
}
}
}
module.exports.AudioManager = AudioManager;
================================================
FILE: lib/audio/client/messages-listener.js
================================================
/**
*
* Reldens - MessagesListener.
*
* Manages audio-related messages from the server and queues them until the scene is ready.
*
*/
const { AudioConst } = require('../constants');
/**
* @typedef {import('colyseus.js').Room} ColyseusRoom
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
*/
class MessagesListener
{
constructor()
{
/** @type {Array} */
this.queueMessages = [];
/** @type {boolean} */
this.sceneReady = false;
}
/**
* @param {ColyseusRoom} room
* @param {GameManager} gameManager
*/
listenMessages(room, gameManager)
{
room.onMessage('*', async (message) => {
await this.processMessage(message, room, gameManager);
});
}
/**
* @returns {Promise}
*/
async processQueue()
{
this.sceneReady = true;
if(0 === this.queueMessages.length){
return false;
}
let messagesToProcess = [...this.queueMessages];
this.queueMessages = [];
for(let messageData of messagesToProcess){
let { message, room, gameManager } = messageData;
await this.processMessage(message, room, gameManager);
}
return true;
}
/**
* @param {Object} message
* @param {ColyseusRoom} room
* @param {GameManager} gameManager
* @returns {Promise}
*/
async processMessage(message, room, gameManager)
{
if(false === this.sceneReady){
this.queueMessages.push({message, room, gameManager});
return;
}
if(message.act === AudioConst.AUDIO_UPDATE){
await gameManager.audioManager.processUpdateData(message, room, gameManager);
}
if(message.act === AudioConst.AUDIO_DELETE){
await gameManager.audioManager.processDeleteData(message, room, gameManager);
}
}
}
module.exports.MessagesListener = MessagesListener;
================================================
FILE: lib/audio/client/plugin.js
================================================
/**
*
* Reldens - Audio Client Plugin
*
* Initializes and manages the audio system on the client side.
*
*/
const { AudioManager } = require('./manager');
const { SceneAudioPlayer } = require('./scene-audio-player');
const { MessagesListener } = require('./messages-listener');
const { AudioUi } = require('./audio-ui');
const { TranslationsMapper } = require('../../snippets/client/translations-mapper');
const Translations = require('./snippets/en_US');
const { PluginInterface } = require('../../features/plugin-interface');
const { AudioConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManagerSingleton} EventsManagerSingleton
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
*/
class AudioPlugin extends PluginInterface
{
/**
* @param {Object} props
* @param {EventsManagerSingleton} [props.events]
* @param {GameManager} [props.gameManager]
*/
setup(props)
{
/** @type {EventsManagerSingleton|boolean} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in AudioPlugin.');
}
/** @type {GameManager|boolean} */
this.gameManager = sc.get(props, 'gameManager', false);
if(!this.gameManager){
Logger.error('Game Manager undefined in AudioPlugin.');
}
this.setTranslations();
/** @type {MessagesListener} */
this.messagesListener = new MessagesListener();
/** @type {typeof SceneAudioPlayer} */
this.sceneAudioPlayer = SceneAudioPlayer;
/** @type {Object} */
this.initialAudiosData = {};
this.listenEvents();
}
/**
* @returns {boolean}
*/
setTranslations()
{
if(!this.gameManager){
return false;
}
TranslationsMapper.forConfig(this.gameManager.config.client, Translations, AudioConst.MESSAGE.DATA_VALUES);
}
/**
* @returns {boolean}
*/
listenEvents()
{
// @TODO - BETA - Extract all listeners handlers in external services.
if(!this.events){
return false;
}
this.events.on('reldens.joinedRoom', (room, gameManager) => {
this.initializeAudioManager(gameManager);
this.messagesListener.listenMessages(room, gameManager);
});
this.events.on('reldens.preloadUiScene', async (preloadScene) => {
preloadScene.load.html('audio', '/assets/html/ui-audio.html');
preloadScene.load.html('audio-category', '/assets/html/ui-audio-category-row.html');
});
this.events.on('reldens.createUiScene', (preloadScene) => {
this.uiManager = new AudioUi(preloadScene);
});
this.events.on('reldens.afterSceneDynamicCreate', async (sceneDynamic) => {
let audioManager = sceneDynamic.gameManager.audioManager;
if(!audioManager){
Logger.warning('Audio manager undefined in AudioPlugin.');
return false;
}
let globalAudios = sc.get(this.initialAudiosData, 'globalAudios', {});
await audioManager.loadGlobalAudios(globalAudios, sceneDynamic);
await this.messagesListener.processQueue();
if(this.uiManager){
let audioSettingInputs = sceneDynamic.gameManager.gameDom.getElements('.audio-setting');
if(0 === audioSettingInputs.length){
this.uiManager.createUi();
}
}
this.sceneAudioPlayer.associateSceneAnimationsAudios(audioManager, sceneDynamic);
sceneDynamic.cameras.main.on('camerafadeincomplete', () => {
this.sceneAudioPlayer.playSceneAudio(audioManager, sceneDynamic);
});
});
this.events.on('reldens.changeSceneDestroyPrevious', (sceneDynamic) => {
sceneDynamic.gameManager.audioManager.destroySceneAudios();
});
this.events.on('reldens.allAudiosLoaded', (audioManager, audios, currentScene) => {
this.sceneAudioPlayer.playSceneAudio(audioManager, currentScene, true);
});
}
/**
* @param {GameManager} gameManager
*/
initializeAudioManager(gameManager)
{
if(gameManager.audioManager){
return;
}
if(!gameManager.initialGameData.player){
Logger.warning('Missing initialGameData.player', gameManager.initialGameData);
}
gameManager.audioManager = new AudioManager({
events: this.events,
currentPlayerData: gameManager.initialGameData.player
});
gameManager.audioManager.updateDefaultConfig(
gameManager.config.getWithoutLogs('client/general/audio/defaultAudioConfig')
);
this.initialAudiosData = sc.get(gameManager.initialGameData, 'audio', {});
}
}
module.exports.AudioPlugin = AudioPlugin;
================================================
FILE: lib/audio/client/scene-audio-player.js
================================================
/**
*
* Reldens - SceneAudioPlayer.
*
* Handles audio playback for scenes and sprite animations in the game.
*
*/
const { AudioConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('./manager').AudioManager} AudioManager
* @typedef {import('phaser').Scene} PhaserScene
* @typedef {import('phaser').GameObjects.Sprite} PhaserSprite
*/
class SceneAudioPlayer
{
/**
* @param {AudioManager} audioManager
* @param {PhaserScene} sceneDynamic
* @param {boolean} [forcePlay]
* @returns {Object|boolean}
*/
playSceneAudio(audioManager, sceneDynamic, forcePlay)
{
let sceneAudio = sceneDynamic['associatedAudio'];
if(
forcePlay !== true
&& (sceneAudio === false || (sceneAudio && sceneAudio.audio.audioInstance.isPlaying))
){
return false;
}
sceneDynamic['associatedAudio'] = audioManager.findAudio(sceneDynamic.key, sceneDynamic.key);
if(sceneDynamic['associatedAudio']){
this.playSpriteAudio(sceneDynamic['associatedAudio'], sceneDynamic, false, audioManager);
return sceneDynamic['associatedAudio'];
}
return false;
}
/**
* @param {AudioManager} audioManager
* @param {PhaserScene} sceneDynamic
*/
associateSceneAnimationsAudios(audioManager, sceneDynamic)
{
sceneDynamic.cameras.main.on('camerafadeincomplete', () => {
if(sceneDynamic.children.list.length <= 0){
return false;
}
for(let sprite of sceneDynamic.children.list){
if(sprite.type !== 'Sprite'){
continue;
}
sprite.on('animationstart', (event) => {
let animationKey = AudioConst.AUDIO_ANIMATION_KEY_START+event.key;
let associatedAudio = this.attachAudioToSprite(sprite, animationKey, audioManager, sceneDynamic);
//Logger.debug('Animation start audio:', animationKey, associatedAudio);
if(false !== associatedAudio){
this.playSpriteAudio(associatedAudio, sceneDynamic, sprite, audioManager);
}
});
sprite.on('animationupdate', (event) => {
let animationKey = AudioConst.AUDIO_ANIMATION_KEY_UPDATE+event.key;
let associatedAudio = this.attachAudioToSprite(sprite, animationKey, audioManager, sceneDynamic);
//Logger.debug('Animation update audio:', animationKey, associatedAudio);
if(false !== associatedAudio){
this.playSpriteAudio(associatedAudio, sceneDynamic, sprite, audioManager);
}
});
sprite.on('animationcomplete', (event) => {
let animationKey = AudioConst.AUDIO_ANIMATION_KEY_COMPLETE+event.key;
let associatedAudio = this.attachAudioToSprite(sprite, animationKey, audioManager, sceneDynamic);
//Logger.debug('Animation complete audio:', animationKey, associatedAudio);
if(false !== associatedAudio){
this.playSpriteAudio(associatedAudio, sceneDynamic, sprite, audioManager);
}
});
sprite.on('animationrepeat', (event) => {
let animationKey = AudioConst.AUDIO_ANIMATION_KEY_REPEAT+event.key;
let associatedAudio = this.attachAudioToSprite(sprite, animationKey, audioManager, sceneDynamic);
//Logger.debug('Animation repeat audio:', animationKey, associatedAudio);
if(false !== associatedAudio){
this.playSpriteAudio(associatedAudio, sceneDynamic, sprite, audioManager);
}
});
sprite.on('animationstop', (event) => {
let animationKey = AudioConst.AUDIO_ANIMATION_KEY_STOP+event.key;
let associatedAudio = this.attachAudioToSprite(sprite, animationKey, audioManager, sceneDynamic);
//Logger.debug('Animation stop audio:', animationKey, associatedAudio);
if(false !== associatedAudio){
this.playSpriteAudio(associatedAudio, sceneDynamic, sprite, audioManager);
}
});
}
});
}
/**
* @param {PhaserSprite} sprite
* @param {string} animationAudioKey
* @param {AudioManager} audioManager
* @param {PhaserScene} sceneDynamic
* @returns {Object|boolean}
*/
attachAudioToSprite(sprite, animationAudioKey, audioManager, sceneDynamic)
{
if(sc.hasOwn(sprite.associatedAudio, animationAudioKey)){
return sprite.associatedAudio[animationAudioKey];
}
if(!sc.hasOwn(sprite, 'associatedAudio')){
sprite.associatedAudio = {};
}
if(!sc.hasOwn(sprite.associatedAudio, animationAudioKey)){
sprite.associatedAudio[animationAudioKey] = audioManager.findAudio(animationAudioKey, sceneDynamic.key);
}
return sprite.associatedAudio[animationAudioKey];
}
/**
* @param {Object} associatedAudio
* @param {PhaserScene} sceneDynamic
* @param {PhaserSprite|boolean} sprite
* @param {AudioManager} audioManager
* @returns {boolean}
*/
playSpriteAudio(associatedAudio, sceneDynamic, sprite, audioManager)
{
let currentPlayerId = Number(audioManager.currentPlayerData.id);
let spritePlayerId = Number(sc.get(sprite, 'player_id'));
//Logger.debug('Play sprite audio.', associatedAudio, sceneDynamic.key, sprite, currentPlayerId);
let isCurrentPlayerSprite = this.isCurrentPlayerSprite(spritePlayerId, currentPlayerId);
if(associatedAudio.audio.data.config?.onlyCurrentPlayer && !isCurrentPlayerSprite){
//Logger.debug('Play sprite audio avoided for current player.', associatedAudio, sceneKey);
return false;
}
let currentPlayer = sceneDynamic.player;
if(isCurrentPlayerSprite && currentPlayer && (currentPlayer.isDisabled() || currentPlayer.isDeath())){
//Logger.debug('Play sprite audio avoided for current dead player.', associatedAudio, sceneKey);
return false;
}
// @NOTE:
// - We need the status update from the actual category in the audio manager the category associated with the
// audio here is just the storage reference.
// - We need to check the category enabled every time the audio can be reproduced because the user can turn
// the category on/off at any time.
if(!associatedAudio || !associatedAudio.audio || !associatedAudio.audio.data){
Logger.error('Missing associated audio data.', associatedAudio);
return false;
}
let audioCategoryKey = associatedAudio.audio.data.related_audio_categories.category_key;
let audioCategory = sc.get(audioManager.categories, audioCategoryKey, false);
let audioEnabled = sc.get(audioManager.playerConfig, audioCategory.id, audioCategory.enabled);
if(!audioCategory || !audioEnabled){
return false;
}
let audioInstance = associatedAudio.audio.audioInstance;
// first stop previous if is single category audio:
if(audioCategory.single_audio){
if(sc.isObjectFunction(audioManager.playing[audioCategory.category_key], 'stop')){
audioManager.playing[audioCategory.category_key].stop();
}
}
// save the new instance in the proper place and play:
// - if is single category then just in the playing property with that category key:
if(audioCategory.single_audio){
audioManager.playing[audioCategory.category_key] = audioInstance;
if(this.isMutedState(audioManager, audioCategory.category_key, audioInstance)){
return false;
}
audioInstance.mute = false;
audioInstance.play();
return true;
}
// - if is not single category:
if(!audioCategory.single_audio){
// - if it does not have a marker, we save the audio instance under the tree category_key > audio_key:
if(!associatedAudio.marker){
audioManager.playing[audioCategory.category_key][associatedAudio.audio.data.audio_key] = audioInstance;
if(this.isMutedState(audioManager, audioCategory.category_key, audioInstance)){
return false;
}
// and play the audio:
audioInstance.mute = false;
audioInstance.play();
return true;
}
// - if it has a marker, we save the audio instance under the tree category_key > marker_key:
if(associatedAudio.marker){
audioManager.playing[audioCategory.category_key][associatedAudio.marker] = audioInstance;
if(this.isMutedState(audioManager, audioCategory.category_key, audioInstance)){
return false;
}
// and play the audio passing the marker:
audioInstance.mute = false;
audioInstance.play(associatedAudio.marker);
return true;
}
}
}
/**
* @param {number} spritePlayerId
* @param {number} currentPlayerId
* @returns {boolean}
*/
isCurrentPlayerSprite(spritePlayerId, currentPlayerId)
{
return spritePlayerId && spritePlayerId === currentPlayerId;
}
/**
* @param {AudioManager} audioManager
* @param {string} mutedKey
* @param {Object} audioInstance
* @returns {boolean}
*/
isMutedState(audioManager, mutedKey, audioInstance)
{
if(false === audioManager.currentMuteState){
return false;
}
Logger.info('AudioManager in muted state to play audio.', {mutedKey, audioInstance});
audioManager.changedMutedState[mutedKey] = audioManager.currentMuteState;
return true;
}
}
module.exports.SceneAudioPlayer = new SceneAudioPlayer();
================================================
FILE: lib/audio/client/snippets/en_US.js
================================================
/**
*
* Reldens - Translations - en_US
*
*/
module.exports = {
audio: {
settingsTitle: 'Audio Settings'
}
}
================================================
FILE: lib/audio/constants.js
================================================
/**
*
* Reldens - AudioConst
*
*/
module.exports.AudioConst = {
AUDIO_UPDATE: 'ap',
AUDIO_DELETE: 'ad',
AUDIO_ANIMATION_KEY_START: 'i_',
AUDIO_ANIMATION_KEY_UPDATE: 'u_',
AUDIO_ANIMATION_KEY_COMPLETE: 'c_',
AUDIO_ANIMATION_KEY_REPEAT: 'r_',
AUDIO_ANIMATION_KEY_STOP: 's_',
AUDIO_BUCKET: '/assets/audio',
MESSAGE: {
DATA: {
UPDATE_TYPE: 'ck',
UPDATE_VALUE: 'uv'
},
DATA_VALUES: {
NAMESPACE: 'audio'
}
}
};
================================================
FILE: lib/audio/server/audio-hot-plug-callbacks.js
================================================
/**
*
* Reldens - AudioHotPlugCallbacks
*
* Provides callback methods for hot-plugging audio files in the admin panel.
*
*/
const { AdminDistHelper } = require('@reldens/cms/lib/admin-dist-helper');
const { sc } = require('@reldens/utils');
class AudioHotPlugCallbacks
{
/**
* @param {Object} projectConfig
* @param {string} bucket
* @param {string} distFolder
* @returns {function|boolean}
*/
static beforeDeleteCallback(projectConfig, bucket, distFolder)
{
if(false === projectConfig.isHotPlugEnabled){
return false;
}
return async (model, id, resource) => {
await this.removeAudio(distFolder, bucket, model, projectConfig, id, resource, true);
};
}
/**
* @param {Object} projectConfig
* @param {string} bucket
* @param {string} distFolder
* @returns {function|boolean}
*/
static beforeUpdateCallback(projectConfig, bucket, distFolder)
{
if(false === projectConfig.isHotPlugEnabled){
return false;
}
return async (model, id, preparedParams, params) => {
let isEnabled = Boolean(sc.get(params, 'enabled', true));
if(isEnabled && preparedParams.files_name !== model.files_name){
await AdminDistHelper.copyBucketFilesToDist(bucket, params.files_name, distFolder);
}
};
}
/**
* @param {Object} projectConfig
* @param {string} bucket
* @param {string} distFolder
* @returns {function|boolean}
*/
static afterUpdateCallback(projectConfig, bucket, distFolder)
{
if(false === projectConfig.isHotPlugEnabled){
return false;
}
return async (model, id, preparedParams, params, originalParams, resource) => {
if(1 < Object.keys(params).length && params === preparedParams){
return false;
}
false === Boolean(model.enabled)
? await this.removeAudio(distFolder, bucket, model, projectConfig, id, resource)
: await this.updateAudio(params, bucket, model, distFolder, projectConfig, preparedParams, resource);
};
}
/**
* @param {Object} params
* @param {string} bucket
* @param {Object} model
* @param {string} distFolder
* @param {Object} projectConfig
* @param {Object} preparedParams
* @param {Object} resource
* @returns {Promise}
*/
static async updateAudio(params, bucket, model, distFolder, projectConfig, preparedParams, resource)
{
let dataServer = projectConfig.serverManager.dataServer;
let fullAudioData = await dataServer.getEntity('audio').loadByIdWithRelations(model.id);
projectConfig.serverManager.audioManager.hotPlugAudio({
newAudioModel: fullAudioData,
preparedParams,
params,
resource
});
}
/**
* @param {string} distFolder
* @param {string} bucket
* @param {Object} model
* @param {Object} projectConfig
* @param {number} id
* @param {Object} resource
* @param {boolean} [removeFiles]
* @returns {Promise}
*/
static async removeAudio(distFolder, bucket, model, projectConfig, id, resource, removeFiles = false)
{
if(true === removeFiles){
await AdminDistHelper.removeBucketAndDistFiles(distFolder, bucket, model.files_name);
}
projectConfig.serverManager.audioManager.hotUnplugAudio({
newAudioModel: model,
id: Number(id),
resource
});
}
}
module.exports.AudioHotPlugCallbacks = AudioHotPlugCallbacks;
================================================
FILE: lib/audio/server/entities/audio-entity-override.js
================================================
/**
*
* Reldens - AudioEntityOverride
*
* Extends the audio entity with custom property configurations and hot-plug callbacks for the admin panel.
*
*/
const { AudioEntity } = require('../../../../generated-entities/entities/audio-entity');
const { AudioHotPlugCallbacks } = require('../audio-hot-plug-callbacks');
const { AllowedFileTypes } = require('../../../game/allowed-file-types');
const { AudioConst } = require('../../constants');
const { FileHandler } = require('@reldens/server-utils');
class AudioEntityOverride extends AudioEntity
{
/**
* @param {Object} extraProps
* @param {Object} projectConfig
* @returns {Object}
*/
static propertiesConfig(extraProps, projectConfig)
{
let config = super.propertiesConfig(extraProps);
let bucket = FileHandler.joinPaths(projectConfig.bucketFullPath, 'assets', 'audio');
let bucketPath = '/assets/audio/';
let distFolder = FileHandler.joinPaths(projectConfig.distPath, 'assets', 'audio');
config.properties.files_name = {
isRequired: true,
isArray: ',',
isUpload: true,
allowedTypes: AllowedFileTypes.AUDIO,
bucket,
bucketPath,
distFolder
};
config.listProperties.splice(config.listProperties.indexOf('config'), 1);
config.navigationPosition = 1100;
config.bucketPath = AudioConst.AUDIO_BUCKET+'/';
config.bucket = bucket;
config.callbacks = {
// @NOTE: we use the update callback because that's when the file_name is updated with the upload plugin.
beforeUpdate: AudioHotPlugCallbacks.beforeUpdateCallback(projectConfig, bucket, distFolder),
afterUpdate: AudioHotPlugCallbacks.afterUpdateCallback(projectConfig, bucket, distFolder),
beforeDelete: AudioHotPlugCallbacks.beforeDeleteCallback(projectConfig, bucket, distFolder)
};
return config;
}
}
module.exports.AudioEntityOverride = AudioEntityOverride;
================================================
FILE: lib/audio/server/entities-config.js
================================================
/**
*
* Reldens - Registered Entities
*
*/
const { AudioEntityOverride } = require('./entities/audio-entity-override');
module.exports.entitiesConfig = {
audio: AudioEntityOverride
};
================================================
FILE: lib/audio/server/entities-translations.js
================================================
/**
*
* Reldens - Entities Translations
*
*/
module.exports.entitiesTranslations = {
labels: {
audio: 'Audios',
audio_categories: 'Categories',
audio_markers: 'Markers',
audio_player_config: 'Players Configuration'
}
};
================================================
FILE: lib/audio/server/manager.js
================================================
/**
*
* Reldens - AudioManager
*
* Manages audio data, categories, and player configurations on the server side.
*
*/
const { AudioConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@colyseus/core').Client} ColyseusClient
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
*
* @typedef {Object} AudioManagerProps
* @property {Object} roomsManager
* @property {Object} dataServer
*/
class AudioManager
{
/**
* @param {AudioManagerProps} props
*/
constructor(props)
{
/** @type {Object} */
this.categories = {};
/** @type {Object} */
this.globalAudios = {};
/** @type {Object} */
this.roomsAudios = {};
/** @type {Object} */
this.roomsManager = props.roomsManager;
/** @type {Object} */
this.dataServer = props.dataServer;
this.setRepositories();
}
/**
* @returns {boolean}
*/
setRepositories()
{
if(!this.dataServer){
Logger.error('DataServer undefined in AudioManager.');
return false;
}
this.audioRepository = this.dataServer.getEntity('audio');
this.audioPlayerConfigRepository = this.dataServer.getEntity('audioPlayerConfig');
this.audioCategoriesRepository = this.dataServer.getEntity('audioCategories');
}
/**
* @returns {Promise}
*/
async loadAudioCategories()
{
// @TODO - BETA - Include config fields in audio categories table.
this.categories = await this.audioCategoriesRepository.loadBy('enabled', 1);
}
/**
* @returns {Promise}
*/
async loadGlobalAudios()
{
if(0 === Object.keys(this.globalAudios).length){
let loadedGlobalAudios = await this.audioRepository.loadWithRelations(
{room_id: null, enabled: 1},
['related_audio_categories', 'related_audio_markers']
);
this.globalAudios = sc.convertObjectsArrayToObjectByKeys(
this.convertAudiosConfigJsonToObjects(loadedGlobalAudios),
'audio_key'
);
}
return this.globalAudios;
}
/**
* @param {Array} loadedGlobalAudios
* @returns {Array}
*/
convertAudiosConfigJsonToObjects(loadedGlobalAudios)
{
for(let audio of loadedGlobalAudios){
if(audio.config){
let convertedData = sc.toJson(audio.config);
if(convertedData){
audio.config = convertedData;
}
}
if(audio.related_audio_markers){
for(let marker of audio.related_audio_markers){
if(marker.config){
let convertedData = sc.toJson(marker.config);
if(convertedData){
marker.config = convertedData;
}
}
}
}
}
return loadedGlobalAudios;
}
/**
* @param {number} roomId
* @returns {Promise}
*/
async loadRoomAudios(roomId)
{
if(!sc.hasOwn(this.roomsAudios, roomId)){
let loadedRoomAudios = await this.audioRepository.loadWithRelations(
{room_id: roomId, enabled: 1},
['related_rooms', 'related_audio_categories', 'related_audio_markers']
);
this.roomsAudios[roomId] = sc.convertObjectsArrayToObjectByKeys(
this.convertAudiosConfigJsonToObjects(loadedRoomAudios),
'audio_key'
);
}
return this.roomsAudios[roomId];
}
/**
* @param {number} playerId
* @returns {Promise}
*/
async loadAudioPlayerConfig(playerId)
{
// @TODO - BETA - Improve login performance, avoid query by getting config from existent player schema.
let configModels = await this.audioPlayerConfigRepository.loadBy('player_id', playerId);
if(0 === configModels.length){
return {};
}
let playerConfig = {};
for(let config of configModels) {
playerConfig[config['category_id']] = config['enabled'];
}
return playerConfig;
}
/**
* @param {ColyseusClient} client
* @param {Object} message
* @param {RoomScene} room
* @returns {Promise}
*/
async executeMessageActions(client, message, room)
{
if(message.act !== AudioConst.AUDIO_UPDATE){
return false;
}
let currentPlayer = room.playerBySessionIdFromState(client.sessionId);
let audioCategory = await this.audioCategoriesRepository.loadOneBy(
'category_key',
message[AudioConst.MESSAGE.DATA.UPDATE_TYPE]
);
if(!currentPlayer || !currentPlayer.player_id || !audioCategory){
return false;
}
let filters = {
player_id: currentPlayer.player_id,
category_id: audioCategory.id
};
let playerConfig = await this.audioPlayerConfigRepository.loadOne(filters);
let updatePatch = {enabled: (message[AudioConst.MESSAGE.DATA.UPDATE_VALUE] ? 1 : 0)};
if(playerConfig){
return await this.audioPlayerConfigRepository.update(filters, updatePatch);
}
return await this.audioPlayerConfigRepository.create(Object.assign(updatePatch, filters));
}
/**
* @param {Object} options
* @returns {boolean}
*/
hotPlugAudio(options)
{
if(options?.newAudioModel?.room_id){
return this.hotPlugGlobalAudio(options?.newAudioModel);
}
return this.hotPlugRoomAudio(options?.newAudioModel);
}
/**
* @param {Object} newAudioModel
* @returns {boolean}
*/
hotPlugRoomAudio(newAudioModel)
{
let roomId = newAudioModel.room_id;
if(!sc.hasOwn(this.roomsAudios, roomId)){
this.roomsAudios[roomId] = {};
}
this.roomsAudios[roomId][newAudioModel.id] = newAudioModel;
let roomInstance = this.findRoom(roomId, this.roomsManager.createdInstances);
if(!roomInstance){
// @NOTE: since the room could not be created yet (because none is connected), we don't need to broadcast
// the new audio, it will be loaded automatically when the room is created.
return true;
}
roomInstance.broadcast('*', {act: AudioConst.AUDIO_UPDATE, roomId, audios: [newAudioModel]});
}
/**
* @param {Object} newAudioModel
* @returns {boolean}
*/
hotPlugGlobalAudio(newAudioModel)
{
this.globalAudios[newAudioModel.id] = newAudioModel;
let createdRooms = Object.keys(this.roomsManager.createdInstances);
if(0 === createdRooms.length){
return false;
}
for(let i of createdRooms){
let roomInstance = this.roomsManager.createdInstances[i];
let broadcastData = {
act: AudioConst.AUDIO_UPDATE,
roomId: i,
audios: {}
};
broadcastData['audios'][newAudioModel.id] = newAudioModel;
roomInstance.broadcast('*', broadcastData);
}
}
/**
* @param {Object} props
* @returns {boolean}
*/
hotUnplugAudio(props)
{
let {newAudioModel, id} = props;
if(newAudioModel.room_id){
return this.hotUnplugRoomAudio(newAudioModel, id);
}
return this.hotUnplugGlobalAudio(newAudioModel, id);
}
/**
* @param {Object} newAudioModel
* @param {number} id
* @returns {boolean}
*/
hotUnplugRoomAudio(newAudioModel, id)
{
let roomAudiosList = sc.get(this.roomsAudios, newAudioModel.room_id, false);
if(false !== roomAudiosList && sc.hasOwn(roomAudiosList, id)){
delete this.roomsAudios[newAudioModel.room_id][id];
}
let roomInstance = this.findRoom(newAudioModel.room_id, this.roomsManager.createdInstances);
if(!roomInstance){
return true;
}
let data = {
act: AudioConst.AUDIO_DELETE,
roomId: newAudioModel.room_id,
audios: {}
};
data['audios'][newAudioModel.id] = newAudioModel;
roomInstance.broadcast('*', data);
}
/**
* @param {Object} newAudioModel
* @param {number} id
* @returns {boolean}
*/
hotUnplugGlobalAudio(newAudioModel, id)
{
delete this.globalAudios[id];
let createdRooms = Object.keys(this.roomsManager.createdInstances);
if(0 === createdRooms.length){
return false;
}
for(let i of createdRooms){
let roomInstance = this.roomsManager.createdInstances[i];
let broadcastData = {
act: AudioConst.AUDIO_DELETE,
roomId: i,
audios: {}
};
broadcastData['audios'][newAudioModel.id] = newAudioModel;
roomInstance.broadcast('*', broadcastData);
}
}
/**
* @param {number} roomId
* @param {Object} [instancesList]
* @returns {Object|boolean}
*/
findRoom(roomId, instancesList = {})
{
let roomInstances = Object.keys(instancesList);
if(0 === roomInstances.length){
return false;
}
for(let i of roomInstances){
let room = instancesList[i];
if(!room.roomData){
continue;
}
if(room.roomData.roomId === roomId){
return room;
}
}
return false;
}
}
module.exports.AudioManager = AudioManager;
================================================
FILE: lib/audio/server/plugin.js
================================================
/**
*
* Reldens - Audio Server Plugin
*
* Initializes and manages the audio system on the server side.
*
*/
const { PluginInterface } = require('../../features/plugin-interface');
const { AudioManager } = require('./manager');
const { AudioConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManagerSingleton} EventsManagerSingleton
*/
class AudioPlugin extends PluginInterface
{
constructor()
{
super();
/** @type {AudioManager|boolean} */
this.audioManager = false;
}
/**
* @param {Object} props
* @param {EventsManagerSingleton} [props.events]
* @param {Object} [props.dataServer]
*/
setup(props)
{
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in AudioPlugin.');
}
this.dataServer = sc.get(props, 'dataServer', false);
if(!this.dataServer){
Logger.error('DataServer undefined in AudioPlugin.');
}
this.events.on('reldens.serverBeforeDefineRooms', async (props) => {
this.audioManager = new AudioManager({
roomsManager: props.serverManager.roomsManager,
dataServer: this.dataServer
});
await this.audioManager.loadAudioCategories();
await this.audioManager.loadGlobalAudios();
props.serverManager.audioManager = this.audioManager;
});
this.events.on('reldens.defineRoomsInGameServerDone', async (roomsManager) => {
let definedRoomsNames = Object.keys(roomsManager.definedRooms);
if(definedRoomsNames.length){
for(let roomName of definedRoomsNames){
let definedRoomData = roomsManager.definedRooms[roomName];
if(!definedRoomData.roomProps.roomData || !definedRoomData.roomProps.roomData.roomId){
continue;
}
await this.audioManager.loadRoomAudios(definedRoomData.roomProps.roomData.roomId);
}
}
});
this.events.on('reldens.beforeSuperInitialGameData', (superInitialGameData) => {
superInitialGameData.audio = {globalAudios: this.audioManager.globalAudios};
});
this.events.on('reldens.createPlayerAfter', async (client, userModel, currentPlayer, roomScene) => {
let playerConfig = await this.audioManager.loadAudioPlayerConfig(currentPlayer.player_id);
let data = {
act: AudioConst.AUDIO_UPDATE,
roomId: roomScene.roomData.roomId,
audios: this.audioManager.roomsAudios[roomScene.roomData.roomId],
categories: this.audioManager.categories,
playerConfig: playerConfig
};
client.send('*', data);
});
this.events.on('reldens.roomsMessageActionsGlobal', (roomMessageActions) => {
roomMessageActions.audio = this.audioManager;
});
}
}
module.exports.AudioPlugin = AudioPlugin;
================================================
FILE: lib/bundlers/drivers/parcel-config.json
================================================
{
"bundler": "@parcel/bundler-default",
"transformers": {
"types:*.{ts,tsx}": ["@parcel/transformer-typescript-types"],
"bundle-text:*": ["...", "@parcel/transformer-inline-string"],
"data-url:*": ["...", "@parcel/transformer-inline-string"],
"worklet:*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
"@parcel/transformer-worklet",
"..."
],
"*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
"@parcel/transformer-babel",
"@parcel/transformer-js",
"@parcel/transformer-react-refresh-wrap"
],
"*.{json,json5}": ["@parcel/transformer-json"],
"*.jsonld": ["@parcel/transformer-jsonld"],
"*.webmanifest": ["@parcel/transformer-webmanifest"],
"webmanifest:*.{json,webmanifest}": ["@parcel/transformer-webmanifest"],
"*.{yaml,yml}": ["@parcel/transformer-yaml"],
"*.{gql,graphql}": ["@parcel/transformer-graphql"],
"*.{sass,scss}": ["@parcel/transformer-sass"],
"*.less": ["@parcel/transformer-less"],
"*.{css,pcss}": ["@parcel/transformer-postcss", "@parcel/transformer-css"],
"*.sss": ["@parcel/transformer-sugarss"],
"*.{htm,html,xhtml}": [
"@parcel/transformer-posthtml",
"@parcel/transformer-html"
],
"*.pug": ["@parcel/transformer-pug"],
"*.coffee": ["@parcel/transformer-coffeescript"],
"*.vue": ["@parcel/transformer-vue"],
"template:*.vue": ["@parcel/transformer-vue"],
"script:*.vue": ["@parcel/transformer-vue"],
"style:*.vue": ["@parcel/transformer-vue"],
"custom:*.vue": ["@parcel/transformer-vue"],
"*.{png,jpg,jpeg,webp,gif,tiff,avif,heic,heif}": [
"@parcel/transformer-image"
],
"*.svg": ["@parcel/transformer-svg"],
"*.{xml,rss,atom}": ["@parcel/transformer-xml"],
"url:*": ["...", "@parcel/transformer-raw"]
},
"namers": ["@parcel/namer-default"],
"runtimes": [
"@parcel/runtime-rsc",
"@parcel/runtime-js",
"@parcel/runtime-browser-hmr",
"@parcel/runtime-service-worker"
],
"optimizers": {
"data-url:*": ["...", "@parcel/optimizer-data-url"],
"*.css": ["@parcel/optimizer-css"],
"*.{html,xhtml}": ["@parcel/optimizer-htmlnano"],
"*.{js,mjs,cjs}": ["@parcel/optimizer-swc"],
"*.{jpg,jpeg,png}": ["@parcel/optimizer-image"]
},
"packagers": {
"*.{html,xhtml}": "@parcel/packager-html",
"*.css": "@parcel/packager-css",
"*.{js,mjs,cjs}": "@parcel/packager-js",
"*.svg": "@parcel/packager-svg",
"*.{xml,rss,atom}": "@parcel/packager-xml",
"*.ts": "@parcel/packager-ts",
"*.wasm": "@parcel/packager-wasm",
"*.{jsonld,svg,webmanifest}": "@parcel/packager-raw-url",
"*": "@parcel/packager-raw"
},
"compressors": {
"*": ["@parcel/compressor-raw"]
},
"resolvers": ["@parcel/resolver-default"]
}
================================================
FILE: lib/chat/cleaner.js
================================================
/**
*
* Reldens - Cleaner
*
* Handles chat message cleaning and validation.
*
*/
const { sc } = require('@reldens/utils');
class Cleaner
{
/**
* @param {string} message
* @param {number} characterLimit
* @returns {string}
*/
cleanMessage(message, characterLimit)
{
// @TODO - BETA - Implement any clean feature here.
return sc.cleanMessage(message, characterLimit);
}
}
module.exports.Cleaner = new Cleaner();
================================================
FILE: lib/chat/client/chat-tabs.js
================================================
/**
*
* Reldens - ChatTabs
*
* Manages chat tabs creation and activation for different message types.
*
*/
const { ChatConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
* @typedef {import('phaser').Scene} PhaserScene
*/
class ChatTabs
{
/**
* @param {GameManager} gameManager
* @param {PhaserScene} uiScene
*/
constructor(gameManager, uiScene)
{
/** @type {GameManager} */
this.gameManager = gameManager;
/** @type {PhaserScene} */
this.uiScene = uiScene;
/** @type {boolean} */
this.showTabs = this.gameManager.config.get('client/ui/chat/showTabs');
/** @type {string|boolean} */
this.containerTemplate = false;
/** @type {string|boolean} */
this.headerTemplate = false;
/** @type {string|boolean} */
this.contentTemplate = false;
this.createTabs();
this.activateTabs();
}
/**
* @returns {boolean}
*/
createTabs()
{
if(!this.isReady()){
return false;
}
let chatTypes = sc.get(this.gameManager.initialGameData, 'chatTypes', []);
if(0 === chatTypes.length){
Logger.info('Chat types empty.');
return false;
}
let chatContentsElement = this.gameManager.gameDom.getElement(ChatConst.SELECTORS.CONTENTS);
if(!chatContentsElement){
Logger.info('Chat contents element not found.');
return false;
}
if(!this.fetchTemplates()){
return false;
}
let tabsHeaders = '';
let tabsContents = '';
let i = 0;
for(let chatType of chatTypes){
if(!chatType.show_tab){
continue;
}
let tabKey = chatType.key;
let tabId = chatType.id;
let headerClone = Object.assign({}, {headerTemplate: this.headerTemplate});
tabsHeaders += this.gameManager.gameEngine.parseTemplate(
headerClone.headerTemplate,
{
tabId,
tabLabel: this.gameManager.services.translator.t(
ChatConst.SNIPPETS.PREFIX+ChatConst.SNIPPETS.TAB_PREFIX+tabKey
),
className: 0 === i ? ' active' : ''
}
);
let contentClone = Object.assign({}, {contentTemplate: this.contentTemplate});
tabsContents += this.gameManager.gameEngine.parseTemplate(
contentClone.contentTemplate,
{
tabId,
tabKey,
className: 0 === i ? ' active' : ''
}
);
i++;
}
let tabs = this.gameManager.gameEngine.parseTemplate(this.containerTemplate, {tabsHeaders, tabsContents});
this.gameManager.gameDom.updateContent(ChatConst.SELECTORS.CONTENTS, tabs);
}
/**
* @returns {boolean}
*/
fetchTemplates()
{
this.containerTemplate = this.uiScene.cache.html.get('chatTabsContainer');
if(!this.containerTemplate){
Logger.info('Chat containerTemplate not found.');
return false;
}
this.headerTemplate = this.uiScene.cache.html.get('chatTabLabel');
if(!this.headerTemplate){
Logger.info('Chat headerTemplate not found.');
return false;
}
this.contentTemplate = this.uiScene.cache.html.get('chatTabContent');
if(!this.contentTemplate){
Logger.info('Chat contentTemplate not found.');
return false;
}
return true;
}
/**
* @returns {boolean}
*/
isReady()
{
if(!this.gameManager){
Logger.error('ChatTabs, missing game manager.');
}
if(!this.uiScene){
Logger.error('ChatTabs, missing UI Scene.');
}
return !(!this.showTabs || !this.gameManager || !this.uiScene);
}
activateTabs()
{
let labels = this.gameManager.gameDom.getElements('.tab-label');
for(let label of labels){
label.addEventListener('click', (event) => {
let previousLabel = this.gameManager.gameDom.getElement('.tab-label.active');
previousLabel?.classList.remove('active');
event.target.classList.add('active');
let previousContent = this.gameManager.gameDom.getElement('.tab-content.active');
previousContent?.classList.remove('active');
let activate = this.gameManager.gameDom.getElement('.tab-content-'+event.target.dataset.tabId);
if(!activate){
Logger.warning('Tab content was not found.', event);
return false;
}
activate.classList.add('active');
activate.parentNode.scrollTop = activate.scrollHeight;
});
}
}
}
module.exports.ChatTabs = ChatTabs;
================================================
FILE: lib/chat/client/chat-ui.js
================================================
/**
*
* Reldens - ChatUi
*
* Manages the chat user interface including message display, tabs, and overhead chat.
*
*/
const { Input } = require('phaser');
const { SpriteTextFactory } = require('../../game/client/engine/sprite-text-factory');
const { ChatTabs } = require('./chat-tabs');
const { ChatConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('phaser').Scene} PhaserScene
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
* @typedef {import('../../game/client/game-dom').GameDom} GameDom
* @typedef {import('phaser').GameObjects.Sprite} PhaserSprite
*/
class ChatUi
{
/**
* @param {PhaserScene} uiScene
*/
constructor(uiScene)
{
/** @type {PhaserScene} */
this.uiScene = uiScene;
/** @type {GameManager} */
this.gameManager = this.uiScene?.gameManager;
/** @type {GameDom} */
this.gameDom = this.uiScene?.gameManager?.gameDom;
this.setChatTypes();
this.setChatConfiguration();
/** @type {Object} */
this.uiChat = {};
/** @type {Array} */
this.messagesQueu = [];
/** @type {HTMLElement|boolean} */
this.chatInput = false;
/** @type {HTMLElement|boolean} */
this.chatSendButton = false;
/** @type {HTMLElement|boolean} */
this.chatCloseButton = false;
/** @type {HTMLElement|boolean} */
this.chatOpenButton = false;
}
/**
* @returns {boolean}
*/
setChatConfiguration()
{
if(!this.gameManager || !this.gameManager.config){
return false;
}
this.uiConfig = this.gameManager.config.get('client/ui/chat');
this.overheadChat = sc.get(this.uiConfig, 'overheadChat', {});
this.overHeadChatEnabled = sc.get(this.overheadChat, 'enabled', false);
this.overheadText = sc.get(this.uiConfig, 'overheadText', {});
this.isDefaultOpen = sc.get(this.uiConfig, 'defaultOpen', false);
this.isTyping = sc.get(this.overheadChat, 'isTyping', false);
this.showTabs = sc.get(this.uiConfig, 'showTabs', false);
this.closeChatBoxAfterSend = sc.get(this.closeChatBoxAfterSend, 'isTyping', false);
this.messagesConfig = this.gameManager.config.get('client/chat/messages');
this.characterLimit = sc.get(this.messagesConfig, 'characterLimit', 0);
this.characterLimitOverhead = sc.get(this.messagesConfig, 'characterLimitOverhead', 0);
this.appendErrorTypeOnActiveTab = sc.get(this.messagesConfig, 'appendErrorTypeOnActiveTab', true);
}
/**
* @returns {boolean}
*/
setChatTypes()
{
if(!this.gameManager){
Logger.warning('Missing GameManager on ChatUI.');
return false;
}
if(!this.gameDom){
Logger.warning('Missing GameDom on ChatUI.');
return false;
}
if(!this.gameManager.initialGameData){
Logger.warning('Missing "initialGameData" on ChatUI.');
return false;
}
this.chatTypes = sc.get(this.gameManager.initialGameData, 'chatTypes', []);
if(0 === this.chatTypes.length){
return false;
}
this.chatTypesById = {};
for(let chatType of this.chatTypes){
this.chatTypesById[chatType.id] = chatType;
}
}
/**
* @returns {boolean}
*/
createUi()
{
if(!this.uiScene){
Logger.warning('Missing UI Scene on ChatUI.');
return false;
}
// @TODO - BETA - Replace by UserInterface.
let {uiX, uiY} = this.uiScene.getUiConfig('chat');
this.uiChat = this.uiScene.add.dom(uiX, uiY).createFromCache('chat');
this.uiScene.elementsUi['chat'] = this.uiChat;
this.chatInput = this.uiChat.getChildByProperty('id', ChatConst.CHAT_INPUT);
if(!this.chatInput){
Logger.info('Missing chat input on ChatUI.');
return false;
}
this.setupKeyPressBehaviors();
this.chatInput.addEventListener('onfocusout', () => {
this.hideIsTyping();
});
this.setupSendButton();
this.setupOpenCloseButtons();
if(this.overHeadChatEnabled){
this.setupOverheadChatEvents();
}
if(this.isDefaultOpen){
this.showChatBox();
}
}
/**
* @returns {boolean}
*/
createTabs()
{
if(!this.showTabs){
return false;
}
this.tabs = new ChatTabs(this.gameManager, this.uiScene);
return true;
}
setupOverheadChatEvents()
{
this.gameManager.events.on('reldens.runPlayerAnimation', (playerEngine, playerId, playerState, playerSprite) => {
this.updateOverheadTextPosition(playerSprite);
});
}
setupOpenCloseButtons()
{
this.chatOpenButton = this.uiChat.getChildByProperty('id', ChatConst.CHAT_OPEN_BUTTON);
this.chatOpenButton?.addEventListener('click', () => {
this.showChatBox();
this.gameManager.events.emit(
'reldens.openUI',
{
ui: this,
openButton: this.chatOpenButton,
dialogBox: this.uiChat,
dialogContainer: this.uiChat.getChildByProperty('id', ChatConst.CHAT_UI),
uiScene: this.uiScene
}
);
});
this.chatCloseButton = this.uiChat.getChildByProperty('id', ChatConst.CHAT_CLOSE_BUTTON);
this.chatCloseButton?.addEventListener('click', () => {
this.hideChatBox();
this.gameManager.events.emit(
'reldens.closeUI',
{
ui: this,
closeButton: this.chatCloseButton,
openButton: this.chatOpenButton,
dialogBox: this.uiChat,
dialogContainer: this.uiChat.getChildByProperty('id', ChatConst.CHAT_UI),
uiScene: this.uiScene
}
);
});
}
setupSendButton()
{
this.chatSendButton = this.uiChat.getChildByProperty('id', ChatConst.CHAT_SEND_BUTTON);
this.chatSendButton?.addEventListener('click', (event) => {
event.preventDefault();
this.sendChatMessage(this.chatInput, this.gameManager.activeRoomEvents);
this.chatInput.focus();
});
}
setupKeyPressBehaviors()
{
this.uiScene.input.keyboard.on('keyup-ENTER', () => {
if(!this.isFocussedOnChatInput()){
this.showChatBox();
this.chatInput.focus();
}
});
this.uiScene.input.keyboard.on('keyup-ESC', () => {
if(this.isFocussedOnChatInput()){
this.hideChatBox();
this.chatInput.blur();
}
});
this.chatInput.addEventListener('keyup', (event) => {
if(event.keyCode === Input.Keyboard.KeyCodes.ENTER){
event.preventDefault();
this.sendChatMessage();
return;
}
this.showIsTyping();
});
}
/**
* @param {PhaserSprite} playerSprite
* @param {string} message
* @returns {boolean}
*/
showOverheadChat(playerSprite, message)
{
if(!this.overHeadChatEnabled){
return false;
}
if(playerSprite['overheadTextSprite']){
this.destroyTextSprite(playerSprite);
}
message = this.applyTextLimit(message, this.characterLimitOverhead);
playerSprite['overheadTextSprite'] = SpriteTextFactory.attachTextToSprite(
playerSprite,
message,
this.overheadText,
sc.get(this.overheadText, 'topOff', 0),
'overheadTextSprite',
this.gameManager.getActiveScene()
);
let timeOut = sc.get(this.overheadText, 'timeOut', false);
if(timeOut){
setTimeout(() => {
this.destroyTextSprite(playerSprite);
}, timeOut);
}
}
/**
* @param {PhaserSprite} playerSprite
* @returns {boolean}
*/
updateOverheadTextPosition(playerSprite)
{
if(!playerSprite['overheadTextSprite']){
return false;
}
let relativePosition = SpriteTextFactory.getTextPosition(
playerSprite,
playerSprite.playerName,
this.overheadText,
sc.get(this.overheadText, 'topOff', 0)
);
playerSprite['overheadTextSprite'].x = relativePosition.x;
playerSprite['overheadTextSprite'].y = relativePosition.y;
}
/**
* @param {PhaserSprite} playerSprite
* @returns {boolean}
*/
destroyTextSprite(playerSprite)
{
if(!playerSprite['overheadTextSprite']){
return false;
}
playerSprite['overheadTextSprite'].destroy();
delete playerSprite['overheadTextSprite'];
}
/**
* @returns {boolean}
*/
showIsTyping()
{
if(!this.overHeadChatEnabled || !this.isTyping){
return false;
}
if(!this.isFocussedOnChatInput()){
return false;
}
this.showOverheadChat(
this.gameManager.getCurrentPlayerAnimation(),
this.gameManager.config.getWithoutLogs(
'client/ui/chat/waitingContent',
this.t(ChatConst.SNIPPETS.WAITING)
)
);
}
/**
* @returns {boolean}
*/
hideIsTyping()
{
if(!this.isTyping){
return false;
}
this.destroyTextSprite(this.gameManager.getCurrentPlayerAnimation());
}
/**
* @returns {boolean}
*/
isFocussedOnChatInput()
{
return this.gameManager.gameDom.activeElement() === this.chatInput;
}
showChatBox()
{
let box = this.uiChat.getChildByProperty('id', ChatConst.CHAT_UI);
box.classList.remove('hidden');
this.uiChat.setDepth(4);
this.chatOpenButton?.classList.add('hidden');
let readPanel = this.gameDom.getElement(ChatConst.SELECTORS.CHAT_MESSAGES);
if(readPanel){
readPanel.parentNode.scrollTop = readPanel.scrollHeight;
}
this.hideNotificationsBalloon();
}
/**
* @returns {boolean}
*/
hideChatBox()
{
let box = this.uiChat.getChildByProperty('id', ChatConst.CHAT_UI);
if(!box){
Logger.info('Chat UI box not found.');
return false;
}
box.classList.add('hidden');
this.uiChat.setDepth(1);
this.chatOpenButton?.classList.remove('hidden');
}
showNotificationBalloon()
{
this.getActiveBalloon()?.classList.remove('hidden');
}
hideNotificationsBalloon()
{
this.getActiveBalloon()?.classList.add('hidden');
}
/**
* @returns {HTMLElement|boolean}
*/
getActiveBalloon()
{
if(!sc.get(this.uiConfig, 'notificationBalloon')){
return false;
}
let chatBalloon = this.uiChat.getChildByProperty('id', ChatConst.CHAT_BALLOON);
if(!chatBalloon){
return false;
}
return chatBalloon;
}
/**
* @param {Array} messages
* @returns {boolean}
*/
processMessagesQueue(messages)
{
if(0 === messages.length){
return false;
}
for(let message of messages){
this.attachNewMessage(message);
}
}
/**
* @param {Object} message
*/
attachNewMessage(message)
{
if(!this.gameManager.gameEngine.uiScene.cache){
// expected while uiScene is being created:
// Logger.info('Missing uiScene cache on chat message.', message);
return;
}
let messageTemplate = this.gameManager.gameEngine.uiScene.cache.html.get('chatMessage');
let messageString = this.translateMessage(message);
let output = this.gameManager.gameEngine.parseTemplate(messageTemplate, {
from: this.translateFrom(message),
color: ChatConst.TYPE_COLOR[message[ChatConst.TYPES.KEY]],
message: messageString
});
let defaultType = this.showTabs ? ChatConst.TYPES.MESSAGE : '';
let messageType = sc.get(message, ChatConst.TYPES.KEY, defaultType);
let chatType = sc.get(this.chatTypesById, messageType, false);
let appendToTab = '' !== messageType && chatType?.show_tab
? this.gameManager.gameDom.getElement(ChatConst.SELECTORS.TAB_CONTENT_PREFIX+messageType)
: false;
if(appendToTab){
this.appendWithScroll(appendToTab, output);
}
let alsoShowInTab = chatType.also_show_in_type
? this.gameManager.gameDom.getElement(ChatConst.SELECTORS.TAB_CONTENT_PREFIX+chatType.also_show_in_type)
: false;
if(alsoShowInTab && alsoShowInTab !== appendToTab){
this.appendWithScroll(alsoShowInTab, output);
}
let appendToMain = '' === messageType
? this.gameManager.gameDom.getElement(ChatConst.SELECTORS.CHAT_MESSAGES)
: false;
if(appendToMain){
this.appendWithScroll(appendToMain, output);
}
if(this.appendErrorTypeOnActiveTab && ChatConst.TYPES.ERROR === messageType){
let activeTab = this.gameManager.gameDom.getElement(ChatConst.SELECTORS.TAB_CONTENT_ACTIVE);
if(
activeTab
&& activeTab !== appendToTab
&& activeTab !== alsoShowInTab
){
this.appendWithScroll(activeTab, output);
}
}
if(!appendToTab && !alsoShowInTab && !appendToMain){
if(null === appendToTab){
Logger.warning('Element not found for selector: .tab-content-'+messageType);
}
if(null === alsoShowInTab){
Logger.warning('Element not found for selector: .tab-content-'+chatType.also_show_in_type);
}
Logger.warning('Chat message not attached to any tab or main panel.', {
message,
defaultType,
messageType,
chatType,
appendToMain,
appendToTab
});
return;
}
if(message[ChatConst.MESSAGE.FROM] && this.isValidMessageType(message[ChatConst.TYPES.KEY])){
let playerSprite = this.fetchPlayerByName(message[ChatConst.MESSAGE.FROM]);
if(playerSprite){
this.showOverheadChat(playerSprite, messageString);
}
}
if(!this.uiChat.getChildByProperty('id', ChatConst.CHAT_UI).classList.contains('hidden')){
return;
}
this.showNotificationBalloon();
}
/**
* @param {HTMLElement} appendTo
* @param {string} output
*/
appendWithScroll(appendTo, output)
{
appendTo.innerHTML += output;
appendTo.parentNode.scrollTop = appendTo.scrollHeight;
}
/**
* @param {Object} message
* @returns {string}
*/
translateFrom(message)
{
let messageType = message[ChatConst.TYPES.KEY];
let from = message[ChatConst.MESSAGE.FROM] || ChatConst.TYPES.SYSTEM;
if(!this.isValidSnippetFromType(messageType)){
return from;
}
return this.t(ChatConst.SNIPPETS.PREFIX + ChatConst.TYPES.KEY + messageType);
}
/**
* @param {Object} message
* @returns {string}
*/
translateMessage(message)
{
let messageType = message[ChatConst.TYPES.KEY];
if(!this.isValidSnippetType(messageType)){
return message[ChatConst.MESSAGE.KEY];
}
let messageData = message[ChatConst.MESSAGE.DATA.KEY];
if(!messageData){
return this.t(message[ChatConst.MESSAGE.KEY]);
}
if(messageData[ChatConst.MESSAGE.DATA.MODIFIERS]){
let translatedConcat = '';
let targetLabel = messageData[ChatConst.MESSAGE.DATA.TARGET_LABEL];
let propertyKeys = Object.keys(messageData[ChatConst.MESSAGE.DATA.MODIFIERS]);
for(let propertyKey of propertyKeys){
let propertyLabel = this.t(propertyKey);
let propertyValue = messageData[ChatConst.MESSAGE.DATA.MODIFIERS][propertyKey];
translatedConcat += this.t(message[ChatConst.MESSAGE.KEY], {propertyValue, propertyLabel, targetLabel});
}
return translatedConcat;
}
return this.t(message[ChatConst.MESSAGE.KEY], messageData);
}
/**
* @param {string} snippetKey
* @param {Object} [params]
* @param {boolean} [activeLocale]
* @returns {string}
*/
t(snippetKey, params = {}, activeLocale = false)
{
return this.gameManager.services.translator.t(snippetKey, params, activeLocale);
}
/**
* @param {number} messageType
* @returns {boolean}
*/
isValidMessageType(messageType)
{
return -1 === this.validMessageTypes().indexOf(messageType);
}
/**
* @returns {Array}
*/
validMessageTypes()
{
return [Object.values(ChatConst.TYPES)];
}
/**
* @param {number} messageType
* @returns {boolean}
*/
isValidSnippetType(messageType)
{
let validTypes = this.snippetsMessageTypes();
let typesKeys = Object.keys(validTypes);
for(let typeKey of typesKeys){
let type = validTypes[typeKey];
if(type === messageType){
return true;
}
}
return false;
}
/**
* @returns {Object}
*/
snippetsMessageTypes()
{
let types = Object.assign({}, ChatConst.TYPES);
delete types[ChatConst.TYPES.MESSAGE];
delete types[ChatConst.TYPES.PRIVATE];
delete types[ChatConst.TYPES.GLOBAL];
delete types[ChatConst.TYPES.TEAMS];
return types;
}
/**
* @param {number} from
* @returns {boolean}
*/
isValidSnippetFromType(from)
{
return -1 !== [ChatConst.TYPES.SYSTEM, ChatConst.TYPES.ERROR].indexOf(from);
}
/**
* @param {string} playerName
* @returns {PhaserSprite|boolean}
*/
fetchPlayerByName(playerName)
{
let players = this.gameManager.getCurrentPlayer().players;
let keys = Object.keys(players);
if(1 >= keys.length){
return false;
}
for(let i of keys){
let player = players[i];
if(player.playerName === playerName){
return player;
}
}
}
/**
* @returns {boolean}
*/
sendChatMessage()
{
// validate if there is anything to send:
if(!this.isValidMessage()){
return false;
}
// check if is a global chat (must begin with #) and if the global chat room is ready:
let messageAllowedText = this.applyTextLimit(this.chatInput.value, this.characterLimit);
let message = {act: ChatConst.CHAT_ACTION, m: messageAllowedText};
this.gameManager.events.emitSync('reldens.chatMessageObjectCreated', this, message);
// both global or private messages use the global chat room:
this.useGlobalRoom()
? this.useGlobalRoomForMessage(message)
: this.gameManager.activeRoomEvents.send(message);
// for last empty the input once the message was sent:
this.chatInput.value = '';
if(this.closeChatBoxAfterSend){
this.hideChatBox();
}
}
/**
* @param {string} text
* @param {number} limit
* @returns {string}
*/
applyTextLimit(text, limit)
{
// this is also validated on the server:
return 0 < limit && limit < text.length ? text.substring(0, limit) : text;
}
/**
* @returns {boolean}
*/
useGlobalRoom()
{
return 0 === this.chatInput.value.indexOf('#') || 0 === this.chatInput.value.indexOf('@');
}
/**
* @returns {boolean}
*/
isValidMessage()
{
// this is also validated on the server:
return this.chatInput.value && 0 < this.chatInput.value.replace('#', '').replace('@', '').trim().length;
}
/**
* @param {Object} message
* @returns {boolean}
*/
useGlobalRoomForMessage(message)
{
// if is global check the global chat room:
let globalChat = sc.get(this.gameManager.joinedRooms, ChatConst.CHAT_GLOBAL, false);
if(!globalChat){
Logger.error('Global chat room not found.');
return false;
}
if(0 === this.chatInput.value.indexOf('@')){
this.sendPrivateMessage(message, globalChat);
return;
}
this.globalSend(globalChat, message);
}
/**
* @param {Object} message
* @param {Object} globalChat
* @returns {boolean}
*/
sendPrivateMessage(message, globalChat)
{
let playerName = this.chatInput.value.substring(1, this.chatInput.value.indexOf(' '));
if('@' === playerName){
return false;
}
message.t = playerName;
this.globalSend(globalChat, message);
}
/**
* @param {Object} globalChat
* @param {Object} message
*/
globalSend(globalChat, message)
{
try {
globalChat.send('*', message);
} catch (error) {
Logger.critical(error);
this.gameDom.alertReload(this.gameManager.services.translator.t('game.errors.connectionLost'));
}
}
}
module.exports.ChatUi = ChatUi;
================================================
FILE: lib/chat/client/messages-listener.js
================================================
/**
*
* Reldens - MessagesListener
*
* Listens for chat messages from the server and queues or displays them.
*
*/
const { ChatConst } = require('../constants');
/**
* @typedef {import('colyseus.js').Room} ColyseusRoom
*/
class MessagesListener
{
/**
* @param {ColyseusRoom} room
* @param {Object} chatPack
* @returns {Promise}
*/
static async listenMessages(room, chatPack)
{
room.onMessage('*', (message) => {
if(ChatConst.CHAT_ACTION !== message.act){
return;
}
if(!chatPack.uiManager){
chatPack.messagesQueu.push(message);
return;
}
chatPack.uiManager.attachNewMessage(message);
});
}
}
module.exports.MessagesListener = MessagesListener;
================================================
FILE: lib/chat/client/plugin.js
================================================
/**
*
* Reldens - Chat Client Plugin
*
* Initializes and manages the chat system on the client side.
*
*/
const { ChatUi } = require('./chat-ui');
const { MessagesListener } = require('./messages-listener');
const { TemplatesHandler } = require('./templates-handler');
const Translations = require('./snippets/en_US');
const { TranslationsMapper } = require('../../snippets/client/translations-mapper');
const { PluginInterface } = require('../../features/plugin-interface');
const { ChatConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManagerSingleton} EventsManagerSingleton
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
*/
class ChatPlugin extends PluginInterface
{
/**
* @param {Object} props
* @param {GameManager} [props.gameManager]
* @param {EventsManagerSingleton} [props.events]
*/
async setup(props)
{
/** @type {GameManager|boolean} */
this.gameManager = sc.get(props, 'gameManager', false);
if(!this.gameManager){
Logger.error('Game Manager undefined in ActionsPlugin.');
}
/** @type {EventsManagerSingleton|boolean} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in ChatPlugin.');
}
/** @type {Array} */
this.messagesQueu = [];
/** @type {ChatUi|boolean} */
this.uiManager = false;
/** @type {Array} */
this.joinRooms = [ChatConst.CHAT_GLOBAL];
this.setTranslations();
this.listenEvents();
}
/**
* @returns {boolean}
*/
setTranslations()
{
if(!this.gameManager){
return false;
}
TranslationsMapper.forConfig(this.gameManager.config.client, Translations, ChatConst.MESSAGE.DATA_VALUES);
}
/**
* @returns {boolean}
*/
listenEvents()
{
if(!this.events){
return false;
}
// chat messages are global for all rooms, so we use the generic event for every joined room:
this.events.on('reldens.joinedRoom', async (room) => {
await MessagesListener.listenMessages(room, this);
});
this.events.on('reldens.preloadUiScene', (preloadScene) => {
TemplatesHandler.preloadTemplates(preloadScene, this.gameManager.config.get('client/ui/chat/showTabs'));
});
this.events.on('reldens.createUiScene', (uiScene) => {
this.uiManager = new ChatUi(uiScene);
this.uiManager.createUi();
this.uiManager.createTabs();
this.uiManager.processMessagesQueue(this.messagesQueu);
});
}
}
module.exports.ChatPlugin = ChatPlugin;
================================================
FILE: lib/chat/client/snippets/en_US.js
================================================
/**
*
* Reldens - Translations - en_US
*
*/
module.exports = {
chat: {
ctk3: 'System',
ctk10: 'System',
npcDamage: '%damage damage on %targetLabel',
dodgedSkill: '%targetLabel dodged %skill',
modifiersApply: '%propertyValue %propertyLabel on %targetLabel',
joinedRoom: '%playerName has joined %roomName',
leftRoom: '%playerName has left',
playerNotFound: 'Player "%playerName" not found',
globalMessagesNotAllowed: 'Global messages not allowed',
globalMessagePermissionDenied: 'Global message permission denied',
guestInvalidChangePoint: 'The room is not available for guest users.',
player: {
damage: '%damage damage on %targetLabel',
dodgedSkill: '%targetLabel dodged %skill'
},
tabs: {
message: 'General',
joined: 'Joined',
system: 'System',
private: 'Private',
damage: 'Damage',
reward: 'Rewards',
skill: 'Skills',
teams: 'Teams',
global: 'Global',
error: 'Error'
}
}
}
================================================
FILE: lib/chat/client/templates-handler.js
================================================
/**
*
* Reldens - TemplatesHandler
*
* Preloads chat HTML templates into the Phaser scene cache.
*
*/
/**
* @typedef {import('phaser').Scene} PhaserScene
*/
class TemplatesHandler
{
/**
* @param {PhaserScene} preloadScene
* @param {boolean} showTabs
*/
static preloadTemplates(preloadScene, showTabs)
{
// @TODO - BETA - Replace by loader replacing snake name file name by camel case for the template key.
let chatTemplatePath = '/assets/features/chat/templates/';
// @TODO - BETA - Move the preload HTML as part of the engine driver.
preloadScene.load.html('chat', chatTemplatePath+'ui-chat.html');
preloadScene.load.html('chatMessage', chatTemplatePath+'message.html');
if(showTabs){
preloadScene.load.html('chatTabsContainer', chatTemplatePath+'tabs-container.html');
preloadScene.load.html('chatTabLabel', chatTemplatePath+'tab-label.html');
preloadScene.load.html('chatTabContent', chatTemplatePath+'tab-content.html');
}
}
}
module.exports.TemplatesHandler = TemplatesHandler;
================================================
FILE: lib/chat/constants.js
================================================
/**
*
* Reldens - chat/constants
*
*/
let snippetsPrefix = 'chat.';
let playerPrefix = 'player.';
let tabsPrefix = 'tabs.';
let types = {
KEY: 'ctk',
MESSAGE: 1,
JOINED: 2,
SYSTEM: 3,
PRIVATE: 4,
DAMAGE: 5,
REWARD: 6,
SKILL: 7,
TEAMS: 8,
GLOBAL: 9,
ERROR: 10
};
module.exports.ChatConst = {
ROOM_TYPE_CHAT: 'chat',
CHAT_ACTION: 'c',
TYPES: types,
CHAT_FROM: 'f',
CHAT_TO: 't',
CHAT_UI: 'chat-ui',
CHAT_FORM: 'chat-form',
CHAT_INPUT: 'chat-input',
CHAT_SEND_BUTTON: 'chat-send',
CHAT_CLOSE_BUTTON: 'chat-close',
CHAT_OPEN_BUTTON: 'chat-open',
CHAT_BALLOON: 'notification-balloon',
CHAT_GLOBAL: 'chat',
MESSAGE: {
KEY: 'm',
FROM: 'f',
TO: 't',
DATA: {
KEY: 'md',
SNIPPET: 'sp',
PLAYER_NAME: 'pn',
ROOM_NAME: 'rn',
DAMAGE: 'd',
TARGET_LABEL: 'tL',
SKILL_LABEL: 'sk',
MODIFIERS: 'mfs',
},
DATA_VALUES: {
NAMESPACE: 'chat',
pn: 'playerName',
rn: 'roomName',
d: 'damage',
tL: 'targetLabel',
sk: 'skillLabel',
mfs: 'modifiers'
}
},
SNIPPETS: {
PREFIX: snippetsPrefix,
PLAYER_PREFIX: playerPrefix,
TAB_PREFIX: tabsPrefix,
NPC_DAMAGE: snippetsPrefix+'npcDamage',
NPC_DODGED_SKILL: snippetsPrefix+'dodgedSkill',
MODIFIERS_APPLY: snippetsPrefix+'modifiersApply',
JOINED_ROOM: snippetsPrefix+'joinedRoom',
LEFT_ROOM: snippetsPrefix+'leftRoom',
PRIVATE_MESSAGE_PLAYER_NOT_FOUND: snippetsPrefix+'playerNotFound',
GLOBAL_MESSAGE_NOT_ALLOWED: snippetsPrefix+'globalMessageNotAllowed',
GLOBAL_MESSAGE_PERMISSION_DENIED: snippetsPrefix+'globalMessagePermissionDenied',
PLAYER:{
DAMAGE: snippetsPrefix+playerPrefix+'damage',
DODGED_SKILL: snippetsPrefix+playerPrefix+'dodgedSkill'
},
GUEST_INVALID_CHANGE_POINT: snippetsPrefix+'guestInvalidChangePoint',
WAITING: '...'
},
SELECTORS: {
CONTENTS: '#chat-contents',
CHAT_MESSAGES: '#chat-messages',
TAB_CONTENT_PREFIX: '.tab-content-',
TAB_CONTENT_ACTIVE: '.tab-content.active'
},
TYPE_COLOR: {
[types.MESSAGE]: '#ffffff',
[types.PRIVATE]: '#f39c12',
[types.PRIVATE+'.to']: '#00afff',
[types.GLOBAL]: '#ffff00',
[types.SYSTEM]: '#2ecc71',
[types.ERROR]: '#ff0000',
[types.DAMAGE]: '#ff0000',
[types.SYSTEM+'.modifiers']: '#0feeff',
[types.REWARD]: '#2ecc71',
[types.TEAMS]: '#2ecc71',
}
};
================================================
FILE: lib/chat/message-factory.js
================================================
/**
*
* Reldens - MessageFactory
*
* Creates standardized chat message objects for client-server communication.
*
*/
const { GameConst } = require('../game/constants');
const { ChatConst } = require('./constants');
const { Logger } = require('@reldens/utils');
class MessageFactory
{
/**
* @param {number} type
* @param {string} message
* @param {Object} [messageData]
* @param {string} [from]
* @param {string} [to]
* @returns {Object|boolean}
*/
static create(type, message, messageData = {}, from, to)
{
if(!type){
Logger.error('Missing "type" on MessageFactory.', {type, message});
return false;
}
if(!message){
Logger.error('Missing "message" on MessageFactory.', {type, message});
return false;
}
let messageSendModel = {
[GameConst.ACTION_KEY]: ChatConst.CHAT_ACTION,
[ChatConst.TYPES.KEY]: type,
[ChatConst.MESSAGE.KEY]: message,
};
if(from){
messageSendModel[ChatConst.MESSAGE.FROM] = from;
}
if(to){
messageSendModel[ChatConst.MESSAGE.TO] = to;
}
if(0 < Object.keys(messageData).length){
messageSendModel[ChatConst.MESSAGE.DATA.KEY] = messageData;
}
return messageSendModel;
}
/**
* @param {string} message
* @param {Object} [messageData]
* @returns {string}
*/
static withDataToJson(message, messageData = {})
{
return JSON.stringify(Object.assign({[ChatConst.MESSAGE.KEY]: message.toString()}, messageData));
}
}
module.exports.MessageFactory = MessageFactory;
================================================
FILE: lib/chat/server/entities/chat-entity-override.js
================================================
/**
*
* Reldens - ChatEntityOverride
*
* Extends the chat entity with custom property aliases for the admin panel.
*
*/
const { ChatEntity } = require('../../../../generated-entities/entities/chat-entity');
class ChatEntityOverride extends ChatEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config = this.updateProperty(config, 'player_id', 'alias', 'chat_player_id');
config = this.updateProperty(config, 'room_id', 'alias', 'chat_room');
config = this.updateProperty(config, 'private_player_id', 'alias', 'chat_private_player_id');
config = this.updateProperty(config, 'message_type', 'alias', 'chat_type');
config.navigationPosition = 1000;
config.sort = {direction: 'desc', sortBy: 'id'};
return config;
}
/**
* @param {Object} config
* @param {string} propertyName
* @param {string} propertyField
* @param {string} propertyValue
* @returns {Object}
*/
static updateProperty(config, propertyName, propertyField, propertyValue)
{
config.properties[propertyName][propertyField] = propertyValue;
return config;
}
}
module.exports.ChatEntityOverride = ChatEntityOverride;
================================================
FILE: lib/chat/server/entities/chat-message-types-entity-override.js
================================================
/**
*
* Reldens - ChatMessageTypesEntityOverride
*
* Extends the chat message types entity with custom property aliases for the admin panel.
*
*/
const { ChatMessageTypesEntity } = require('../../../../generated-entities/entities/chat-message-types-entity');
class ChatMessageTypesEntityOverride extends ChatMessageTypesEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.properties['also_show_in_type']['alias'] = 'chat_message_type_id';
return config;
}
}
module.exports.ChatMessageTypesEntityOverride = ChatMessageTypesEntityOverride;
================================================
FILE: lib/chat/server/entities-config.js
================================================
/**
*
* Reldens - Entities Config
*
*/
const { ChatEntityOverride } = require('./entities/chat-entity-override');
const { ChatMessageTypesEntityOverride } = require('./entities/chat-message-types-entity-override');
module.exports.entitiesConfig = {
chat: ChatEntityOverride,
chatMessageTypes: ChatMessageTypesEntityOverride
};
================================================
FILE: lib/chat/server/entities-translations.js
================================================
/**
*
* Reldens - Entities Translations
*
*/
module.exports.entitiesTranslations = {
labels: {
chat: 'Chat',
chatMessageTypes: 'Messages Types'
}
};
================================================
FILE: lib/chat/server/event-listener/guest-invalid-change-point.js
================================================
/**
*
* Reldens - GuestInvalidChangePoint
*
* Sends chat error messages when guest users attempt invalid room changes.
*
*/
const { MessageFactory } = require('../../message-factory');
const { ChatConst } = require('../../constants');
const { Logger } = require('@reldens/utils');
class GuestInvalidChangePoint
{
/**
* @param {Object} event
* @param {Object} chatManager
* @returns {Promise}
*/
async sendMessage(event, chatManager)
{
let message = ChatConst.SNIPPETS.GUEST_INVALID_CHANGE_POINT;
let messageObject = MessageFactory.create(ChatConst.TYPES.ERROR, message, {});
event.contactClient.send('*', messageObject);
await chatManager.saveMessage(
MessageFactory.withDataToJson(message, {}),
event.playerSchema.player_id,
event.playerSchema.state.room_id,
false,
ChatConst.TYPES.ERROR
).catch((error) => {
Logger.error('Save chat message error on player damage callback.', error);
});
}
}
module.exports.GuestInvalidChangePoint = GuestInvalidChangePoint;
================================================
FILE: lib/chat/server/event-listener/npc-skills.js
================================================
/**
*
* Reldens - NpcSkills
*
* Listens for NPC skill events and sends chat messages for damage, modifiers, and dodges.
*
*/
const { NpcDamageCallback } = require('../messages/npc-damage-callback');
const { NpcModifiersCallback } = require('../messages/npc-modifiers-callback');
const { NpcDodgeCallback } = require('../messages/npc-dodge-callback');
const { SkillsEvents, SkillConst } = require('@reldens/skills');
const { sc } = require('@reldens/utils');
class NpcSkills
{
/**
* @param {Object} props
* @param {Object} chatConfig
* @param {Object} chatManager
*/
static listenEvents(props, chatConfig, chatManager)
{
let skillsByType = this.fetchSkillsByType(props, chatConfig);
let attackSkill = sc.get(skillsByType, SkillConst.SKILL.TYPE.ATTACK, null);
let effectSkill = sc.get(skillsByType, SkillConst.SKILL.TYPE.EFFECT, null);
this.listenDamageEvent(attackSkill, chatConfig, chatManager);
this.listenModifiersEvent(effectSkill, chatConfig, chatManager);
this.listenAfterRunLogicEvent((attackSkill || effectSkill), chatConfig, chatManager);
}
/**
* @param {Object} attackSkill
* @param {Object} chatConfig
* @param {Object} chatManager
*/
static listenDamageEvent(attackSkill, chatConfig, chatManager)
{
if(!chatConfig.damageMessages || null === attackSkill){
return;
}
attackSkill.listenEvent(
SkillsEvents.SKILL_ATTACK_APPLY_DAMAGE,
async (skill, target, damage) => {
if(!damage){
return;
}
await NpcDamageCallback.sendMessage({skill, target, damage, chatManager});
},
attackSkill.getOwnerUniqueEventKey('skillAttackApplyDamageChat'),
// @NOTE: objects ownerIdProperty is their uid and that's used as master key for the object event listeners.
attackSkill.owner[attackSkill.ownerIdProperty]
);
}
/**
* @param {Object} effectSkill
* @param {Object} chatConfig
* @param {Object} chatManager
*/
static listenModifiersEvent(effectSkill, chatConfig, chatManager)
{
if(!chatConfig.effectMessages || null === effectSkill){
return;
}
effectSkill.listenEvent(
SkillsEvents.SKILL_EFFECT_TARGET_MODIFIERS,
async (skill) => {
await NpcModifiersCallback.sendMessage({skill, chatManager});
},
effectSkill.getOwnerUniqueEventKey('skillApplyModifiersChat'),
// @NOTE: objects ownerIdProperty is their uid and that's used as master key for the object event listeners.
effectSkill.owner[effectSkill.ownerIdProperty]
);
}
/**
* @param {Object} skillForLogic
* @param {Object} chatConfig
* @param {Object} chatManager
*/
static listenAfterRunLogicEvent(skillForLogic, chatConfig, chatManager)
{
if(!chatConfig.dodgeMessages || null === skillForLogic){
return;
}
skillForLogic.listenEvent(
SkillsEvents.SKILL_AFTER_RUN_LOGIC,
async (skill) => {
if(SkillConst.SKILL_STATES.DODGED !== skill.lastState){
return;
}
await NpcDodgeCallback.sendMessage({skill, chatManager});
},
skillForLogic.getOwnerUniqueEventKey('skillDodgeChat'),
// @NOTE: objects ownerIdProperty is their uid and that's used as master key for the object event listeners.
skillForLogic.owner[skillForLogic.ownerIdProperty]
);
}
/**
* @param {Object} props
* @param {Object} chatConfig
* @returns {Object}
*/
static fetchSkillsByType(props, chatConfig)
{
let skillInstancesList = props.enemyObject?.actions || {};
let keys = Object.keys(skillInstancesList);
if(0 === keys.length){
return {};
}
let skillsByType = {};
for(let i of keys){
let skill = skillInstancesList[i];
if(sc.hasOwn(skillsByType, skill.type)){
continue;
}
if(SkillConst.SKILL.TYPE.ATTACK === skill.type || SkillConst.SKILL.TYPE.ATTACK === skill.parentType){
skillsByType[SkillConst.SKILL.TYPE.ATTACK] = skill;
}
if(SkillConst.SKILL.TYPE.EFFECT === skill.type || SkillConst.SKILL.TYPE.EFFECT === skill.parentType){
skillsByType[SkillConst.SKILL.TYPE.EFFECT] = skill;
}
let totalValidTypes = sc.get(chatConfig, 'totalValidTypes', 2);
if(totalValidTypes === Object.keys(skillsByType).length){
break;
}
}
return skillsByType;
}
}
module.exports.NpcSkills = NpcSkills;
================================================
FILE: lib/chat/server/event-listener/player-skills.js
================================================
/**
*
* Reldens - PlayerSkills
*
* Listens for player skill events and sends chat messages for damage, modifiers, and dodges.
*
*/
const { PlayerDamageCallback } = require('../messages/player-damage-callback');
const { PlayerModifiersCallback } = require('../messages/player-modifiers-callback');
const { PlayerDodgeCallback } = require('../messages/player-dodge-callback');
const { SkillsEvents, SkillConst } = require('@reldens/skills');
class PlayerSkills
{
/**
* @param {Object} classPath
* @param {Object} chatConfig
* @param {Object} chatManager
*/
static listenEvents(classPath, chatConfig, chatManager)
{
this.listenDamageEvent(chatConfig, classPath, chatManager);
this.listenModifiersEvent(chatConfig, classPath, chatManager);
this.listenAfterRunLogicEvent(chatConfig, classPath, chatManager);
}
/**
* @param {Object} chatConfig
* @param {Object} classPath
* @param {Object} chatManager
*/
static listenDamageEvent(chatConfig, classPath, chatManager)
{
if(!chatConfig.damageMessages){
return;
}
classPath.listenEvent(
SkillsEvents.SKILL_ATTACK_APPLY_DAMAGE,
async (skill, target, damage) => {
if(!damage){
return;
}
await PlayerDamageCallback.sendMessage({
skill,
target,
damage,
client: classPath.owner.skillsServer.client.client,
chatManager: chatManager
});
},
classPath.getOwnerUniqueEventKey('skillAttackApplyDamageChat'),
classPath.getOwnerEventKey()
);
}
/**
* @param {Object} chatConfig
* @param {Object} classPath
* @param {Object} chatManager
*/
static listenModifiersEvent(chatConfig, classPath, chatManager)
{
if(!chatConfig.effectMessages){
return;
}
classPath.listenEvent(
SkillsEvents.SKILL_EFFECT_TARGET_MODIFIERS,
async (skill) => {
await PlayerModifiersCallback.sendMessage({
skill,
client: classPath.owner.skillsServer.client.client,
chatManager: chatManager
});
},
classPath.getOwnerUniqueEventKey('skillApplyModifiersChat'),
classPath.getOwnerEventKey()
);
}
/**
* @param {Object} chatConfig
* @param {Object} classPath
* @param {Object} chatManager
*/
static listenAfterRunLogicEvent(chatConfig, classPath, chatManager)
{
if(!chatConfig.dodgeMessages){
return;
}
classPath.listenEvent(
SkillsEvents.SKILL_AFTER_RUN_LOGIC,
async (skill) => {
if(SkillConst.SKILL_STATES.DODGED !== skill.lastState){
return;
}
await PlayerDodgeCallback.sendMessage({
skill,
client: classPath.owner.skillsServer.client.client,
chatManager: chatManager
});
},
classPath.getOwnerUniqueEventKey('skillDodgeChat'),
classPath.getOwnerEventKey()
);
}
}
module.exports.PlayerSkills = PlayerSkills;
================================================
FILE: lib/chat/server/manager.js
================================================
/**
*
* Reldens - ChatManager
*
* Manages chat message persistence and database operations.
*
*/
const { ChatConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
class ChatManager
{
/**
* @param {Object} props
*/
constructor(props)
{
/** @type {Object|boolean} */
this.dataServer = sc.get(props, 'dataServer', false);
/** @type {Object} */
this.chatRepository = this.dataServer?.getEntity('chat');
}
/**
* @param {string} message
* @param {number} playerId
* @param {number} roomId
* @param {Object} clientToPlayerSchema
* @param {number} messageType
* @returns {Promise}
*/
async saveMessage(message, playerId, roomId, clientToPlayerSchema, messageType)
{
if(!this.dataServer){
Logger.error('Data Server undefined in ChatManager.');
}
let entryData = {
player_id: playerId,
message: message,
message_time: sc.getCurrentDate(),
message_type: messageType || ChatConst.TYPES.MESSAGE
};
if(roomId){
entryData.room_id = roomId;
}
if(clientToPlayerSchema && sc.hasOwn(clientToPlayerSchema, 'id')){
entryData.private_player_id = clientToPlayerSchema.state.player_id;
}
try {
let insertResult = await this.chatRepository.create(entryData);
if(!insertResult){
Logger.critical('Chat message insert error.', entryData);
return false;
}
} catch (error) {
Logger.critical('Chat message save error.', entryData, error.message);
return false;
}
return true;
}
}
module.exports.ChatManager = ChatManager;
================================================
FILE: lib/chat/server/message-actions.js
================================================
/**
*
* Reldens - ChatMessageActions
*
* Handles chat message actions including broadcasting and saving messages.
*
*/
const { ChatManager } = require('./manager');
const { MessageFactory } = require('../message-factory');
const { MessagesGuard } = require('./messages-guard');
const { Cleaner } = require('../cleaner');
const { ChatConst } = require('../constants');
const { GameConst } = require('../../game/constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@colyseus/core').Client} ColyseusClient
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
*/
class ChatMessageActions
{
/**
* @param {Object} props
*/
constructor(props)
{
let dataServer = sc.get(props, 'dataServer', false);
if(!dataServer){
Logger.error('DataServer undefined in ChatMessageActions.');
}
/** @type {ChatManager} */
this.chatManager = new ChatManager({dataServer});
}
/**
* @param {ColyseusClient} client
* @param {Object} data
* @param {RoomScene} room
* @param {Object} playerSchema
* @returns {Promise}
*/
async executeMessageActions(client, data, room, playerSchema)
{
await this.chatAction(data, room, playerSchema);
await this.clientJoinAction(data, room, playerSchema);
}
/**
* @param {Object} data
* @param {RoomScene} room
* @param {Object} playerSchema
* @returns {Promise}
*/
async clientJoinAction(data, room, playerSchema)
{
if(!data || !room || !playerSchema){
return false;
}
if(data.act !== GameConst.CLIENT_JOINED || !room.config.get('server/chat/messages/broadcast_join')){
return false;
}
let message = ChatConst.SNIPPETS.JOINED_ROOM;
let messageData = {
[ChatConst.MESSAGE.DATA.PLAYER_NAME]: playerSchema.playerName,
[ChatConst.MESSAGE.DATA.ROOM_NAME]: room.roomName,
};
let messageObject = MessageFactory.create(
ChatConst.TYPES.SYSTEM,
message,
messageData
);
room.broadcast('*', messageObject);
let playerId = playerSchema.player_id;
let roomId = playerSchema.state.room_id;
let saveResult = await this.chatManager.saveMessage(
MessageFactory.withDataToJson(message, messageData),
playerId,
roomId,
false,
ChatConst.TYPES.JOINED
);
if(!saveResult){
Logger.critical('Joined room chat save error.', messageObject, playerId, roomId);
}
}
/**
* @param {Object} data
* @param {RoomScene} room
* @param {Object} playerSchema
* @returns {Promise}
*/
async chatAction(data, room, playerSchema)
{
if(!data || !room || !playerSchema){
return false;
}
if(!MessagesGuard.validate(data)){
return false;
}
let message = Cleaner.cleanMessage(
data[ChatConst.MESSAGE.KEY],
room.config.get('client/chat/messages/characterLimit')
);
let messageData = MessageFactory.create(ChatConst.TYPES.MESSAGE, message, {}, playerSchema.playerName);
room.broadcast('*', messageData);
let playerId = playerSchema.player_id;
let roomId = playerSchema.state.room_id;
let saveResult = await this.chatManager.saveMessage(message, playerId, roomId, false, ChatConst.TYPES.MESSAGE);
if(!saveResult){
Logger.critical('Chat save error.', messageData, playerId, roomId);
}
}
}
module.exports.ChatMessageActions = ChatMessageActions;
================================================
FILE: lib/chat/server/messages/message-data-mapper.js
================================================
/**
*
* Reldens - MessageDataMapper
*
* Maps skill data into a chat message format with modifiers information.
*
*/
const { ChatConst } = require('../../constants');
const { sc } = require('@reldens/utils');
class MessageDataMapper
{
/**
* @param {Object} skill
* @returns {Object|boolean}
*/
static mapMessageWithData(skill)
{
let lastAppliedModifiers = sc.get(skill, 'lastAppliedModifiers', {});
let appliedModifiersKeys = Object.keys(lastAppliedModifiers);
if(0 === appliedModifiersKeys.length){
return false;
}
let isObjectTarget = sc.hasOwn(skill.target, 'key');
let targetLabel = isObjectTarget ? skill.target.title : skill.target.playerName;
let message = ChatConst.SNIPPETS.MODIFIERS_APPLY;
let messageData = {
[ChatConst.MESSAGE.DATA.TARGET_LABEL]: targetLabel,
[ChatConst.MESSAGE.DATA.MODIFIERS]: {}
};
for(let i of appliedModifiersKeys){
let value = lastAppliedModifiers[i];
let property = i.split('/').pop();
messageData[ChatConst.MESSAGE.DATA.MODIFIERS][property] = value;
}
return {message, messageData};
}
}
module.exports.MessageDataMapper = MessageDataMapper;
================================================
FILE: lib/chat/server/messages/npc-damage-callback.js
================================================
/**
*
* Reldens - NpcDamageCallback
*
* Sends chat messages when NPCs deal damage to players or other targets.
*
*/
const { MessageFactory } = require('../../message-factory');
const { Validator } = require('./validator');
const { ChatConst } = require('../../constants');
const { Logger, sc } = require('@reldens/utils');
class NpcDamageCallback
{
/**
* @param {Object} props
* @returns {Promise}
*/
static async sendMessage(props)
{
if(!Validator.validateMessage(props, ['skill', 'chatManager', 'damage', 'target'])){
Logger.error('Invalid message on NpcDamageCallback.', props);
return false;
}
let {skill, target, damage, chatManager} = props;
let client = target?.skillsServer?.client?.client || null;
if(!client){
Logger.error('Client not defined on NpcDamageCallback.', target);
return false;
}
let isObjectTarget = sc.hasOwn(target, 'key');
let targetLabel = isObjectTarget ? target.title : target.playerName;
let isObjectOwner = sc.hasOwn(skill.owner, 'key');
let from = isObjectOwner ? skill.owner.title : skill.owner.playerName;
let message = ChatConst.SNIPPETS.NPC_DAMAGE;
let messageData = {
[ChatConst.MESSAGE.DATA.TARGET_LABEL]: targetLabel,
[ChatConst.MESSAGE.DATA.DAMAGE]: damage,
};
let messageObject = MessageFactory.create(ChatConst.TYPES.DAMAGE, message, messageData, from);
client.send(messageObject);
let playerId = target?.player_id || null;
let roomId = skill.owner?.room_id || null;
let saveResult = await chatManager.saveMessage(
MessageFactory.withDataToJson(message, messageData),
playerId,
roomId,
false,
ChatConst.TYPES.DAMAGE
);
if(!saveResult){
Logger.error('Save chat message error on NPC damage callback.', messageObject, playerId, roomId);
}
return true;
}
}
module.exports.NpcDamageCallback = NpcDamageCallback;
================================================
FILE: lib/chat/server/messages/npc-dodge-callback.js
================================================
/**
*
* Reldens - NpcDodgeCallback
*
* Sends chat messages when NPC skills are dodged by targets.
*
*/
const { MessageFactory } = require('../../message-factory');
const { Validator } = require('./validator');
const { ChatConst } = require('../../constants');
const { Logger, sc } = require('@reldens/utils');
class NpcDodgeCallback
{
/**
* @param {Object} props
* @returns {Promise}
*/
static async sendMessage(props)
{
if(!Validator.validateMessage(props, ['skill', 'chatManager'])){
Logger.error('Invalid message on NpcDodgeCallback.', props);
return false;
}
let {skill, chatManager} = props;
let client = skill.target?.skillsServer?.client?.client || null;
if(!client){
Logger.error('Client not defined on NpcDodgeCallback.', skill);
return false;
}
let isObjectTarget = sc.hasOwn(skill.target, 'key');
let targetLabel = isObjectTarget ? skill.target.title : skill.target.playerName;
let isObjectOwner = sc.hasOwn(skill.owner, 'key');
let from = isObjectOwner ? skill.owner.title : skill.owner.playerName;
let message = ChatConst.SNIPPETS.NPC_DODGED_SKILL;
let messageData = {
[ChatConst.MESSAGE.DATA.TARGET_LABEL]: targetLabel,
[ChatConst.MESSAGE.DATA.SKILL_LABEL]: skill.label,
};
let messageObject = MessageFactory.create(ChatConst.TYPES.SKILL, message, messageData, from);
client.send(messageObject);
let playerId = skill.target?.player_id || null;
let roomId = skill.owner?.room_id || null;
let saveResult = await chatManager.saveMessage(
MessageFactory.withDataToJson(message, messageData),
playerId,
roomId,
false,
ChatConst.TYPES.SKILL
);
if(!saveResult){
Logger.error('Save chat message error on npc dodge callback.', messageObject, playerId, roomId);
}
return true;
}
}
module.exports.NpcDodgeCallback = NpcDodgeCallback;
================================================
FILE: lib/chat/server/messages/npc-modifiers-callback.js
================================================
/**
*
* Reldens - NpcModifiersCallback
*
* Sends chat messages when NPC skills apply modifiers to targets.
*
*/
const { MessageFactory } = require('../../message-factory');
const { MessageDataMapper } = require('./message-data-mapper');
const { Validator } = require('./validator');
const { ChatConst } = require('../../constants');
const { Logger, sc } = require('@reldens/utils');
class NpcModifiersCallback
{
/**
* @param {Object} props
* @returns {Promise}
*/
static async sendMessage(props)
{
if(!Validator.validateMessage(props, ['skill', 'chatManager'])){
Logger.error('Invalid message on NpcModifiersCallback.', props);
return false;
}
let {skill, chatManager} = props;
let client = skill.target?.skillsServer?.client?.client || null;
if(!client){
Logger.info('Client not defined on NpcModifiersCallback.', skill);
return false;
}
let messageWithData = MessageDataMapper.mapMessageWithData(skill);
if(!messageWithData){
return false;
}
let {message, messageData} = messageWithData;
let isObjectOwner = sc.hasOwn(skill.owner, 'key');
let from = isObjectOwner ? skill.owner.title : skill.owner.playerName;
let messageObject = MessageFactory.create(ChatConst.TYPES.SKILL, message, messageData, from);
client.send(messageObject);
let playerId = skill.target?.player_id || null;
let roomId = skill.owner?.room_id || null;
let saveResult = await chatManager.saveMessage(
MessageFactory.withDataToJson(message, messageData),
playerId,
roomId,
false,
ChatConst.TYPES.SKILL
);
if(!saveResult){
Logger.error('Save chat message error on modifiers callback.', messageObject, playerId, roomId);
}
return true;
}
}
module.exports.NpcModifiersCallback = NpcModifiersCallback;
================================================
FILE: lib/chat/server/messages/player-damage-callback.js
================================================
/**
*
* Reldens - PlayerDamageCallback
*
* Sends chat messages when players deal damage to targets.
*
*/
const { MessageFactory } = require('../../message-factory');
const { Validator } = require('./validator');
const { ChatConst } = require('../../constants');
const { Logger, sc } = require('@reldens/utils');
class PlayerDamageCallback
{
/**
* @param {Object} props
* @returns {Promise}
*/
static async sendMessage(props)
{
if(!Validator.validateMessage(props, ['skill', 'client', 'chatManager', 'damage', 'target'])){
Logger.error('Invalid message on PlayerDamageCallback.', props);
return false;
}
let {skill, target, damage, client, chatManager} = props;
if(!client){
Logger.info('Client not defined on PlayerDamageCallback.', skill);
return false;
}
let isObjectTarget = sc.hasOwn(target, 'key');
let targetLabel = isObjectTarget ? target.title : target.playerName;
let message = ChatConst.SNIPPETS.PLAYER.DAMAGE;
let messageData = {
[ChatConst.MESSAGE.DATA.TARGET_LABEL]: targetLabel,
[ChatConst.MESSAGE.DATA.DAMAGE]: damage,
};
let messageObject = MessageFactory.create(ChatConst.TYPES.DAMAGE, message, messageData, skill.owner.playerName);
client.send(messageObject);
let targetClient = skill.target?.skillsServer?.client?.client || null;
if(!isObjectTarget && targetClient && targetClient !== client){
targetClient.send(messageObject);
}
let playerId = skill.owner?.player_id || null;
let roomId = skill.owner?.state?.room_id || null;
let saveResult = await chatManager.saveMessage(
MessageFactory.withDataToJson(message, messageData),
playerId,
roomId,
false,
ChatConst.TYPES.DAMAGE
);
if(!saveResult){
Logger.error('Save chat message error on player damage callback.', messageObject, playerId, roomId);
}
return true;
}
}
module.exports.PlayerDamageCallback = PlayerDamageCallback;
================================================
FILE: lib/chat/server/messages/player-dodge-callback.js
================================================
/**
*
* Reldens - PlayerDodgeCallback
*
* Sends chat messages when player skills are dodged by targets.
*
*/
const { MessageFactory } = require('../../message-factory');
const { Validator } = require('./validator');
const { ChatConst } = require('../../constants');
const { Logger, sc } = require('@reldens/utils');
class PlayerDodgeCallback
{
/**
* @param {Object} props
* @returns {Promise}
*/
static async sendMessage(props)
{
if(!Validator.validateMessage(props, ['skill', 'client', 'chatManager'])){
Logger.error('Invalid message on PlayerDodgeCallback.', props);
return false;
}
let {skill, client, chatManager} = props;
if(!client){
Logger.info('Client not defined on PlayerDamageCallback.', skill);
return false;
}
let isObjectTarget = sc.hasOwn(skill.target, 'key');
let targetLabel = isObjectTarget ? skill.target.title : skill.target.playerName;
let message = ChatConst.SNIPPETS.PLAYER.DODGED_SKILL;
let messageData = {
[ChatConst.MESSAGE.DATA.TARGET_LABEL]: targetLabel,
[ChatConst.MESSAGE.DATA.SKILL_LABEL]: skill.label,
};
let messageObject = MessageFactory.create(ChatConst.TYPES.SKILL, message, messageData, skill.owner.playerName);
client.send(messageObject);
let targetClient = skill.target?.skillsServer?.client?.client || null;
if(!isObjectTarget && targetClient && targetClient !== client){
targetClient.send(messageObject);
}
let playerId = skill.owner?.player_id || null;
let roomId = skill.owner?.state?.room_id || null;
let saveResult = await chatManager.saveMessage(
MessageFactory.withDataToJson(message, messageData),
playerId,
roomId,
false,
ChatConst.TYPES.SKILL
);
if(!saveResult){
Logger.error('Save chat message error on player dodge callback.', messageObject, playerId, roomId);
}
return true;
}
}
module.exports.PlayerDodgeCallback = PlayerDodgeCallback;
================================================
FILE: lib/chat/server/messages/player-modifiers-callback.js
================================================
/**
*
* Reldens - PlayerModifiersCallback
*
* Sends chat messages when player skills apply modifiers to targets.
*
*/
const { MessageFactory } = require('../../message-factory');
const { MessageDataMapper } = require('./message-data-mapper');
const { Validator } = require('./validator');
const { ChatConst } = require('../../constants');
const { Logger, sc} = require('@reldens/utils');
class PlayerModifiersCallback
{
/**
* @param {Object} props
* @returns {Promise}
*/
static async sendMessage(props)
{
if(!Validator.validateMessage(props, ['skill', 'client', 'chatManager'])){
Logger.error('Invalid message on PlayerModifiersCallback.', props);
return false;
}
let {skill, client, chatManager} = props;
if(!client){
Logger.info('Client not defined on PlayerModifiersCallback.', skill);
return false;
}
let messageWithData = MessageDataMapper.mapMessageWithData(skill);
if(!messageWithData){
return false;
}
let {message, messageData} = messageWithData;
let messageObject = MessageFactory.create(ChatConst.TYPES.SKILL, message, messageData, skill.owner.playerName);
client.send(messageObject);
let targetClient = skill.target?.skillsServer?.client?.client || null;
let isObjectTarget = sc.hasOwn(skill.target, 'key');
if(!isObjectTarget && targetClient && targetClient !== client){
targetClient.send(messageObject);
}
let playerId = skill.owner.player_id;
let roomId = skill.owner.state.room_id;
let saveResult = await chatManager.saveMessage(
MessageFactory.withDataToJson(message, messageData),
playerId,
roomId,
false,
ChatConst.TYPES.SKILL
);
if(!saveResult){
Logger.error('Save chat message error on modifiers callback.', messageObject, playerId, roomId);
}
return true;
}
}
module.exports.PlayerModifiersCallback = PlayerModifiersCallback;
================================================
FILE: lib/chat/server/messages/validator.js
================================================
/**
*
* Reldens - Validator
*
* Validates chat message objects have required properties.
*
*/
const { Logger } = require('@reldens/utils');
class Validator
{
/**
* @param {Object} message
* @param {Array} props
* @returns {boolean}
*/
static validateMessage(message, props)
{
if(!message){
Logger.critical('Invalid message');
return false;
}
for(let prop of props){
if(!message[prop]){
Logger.critical('Missing message property: '+prop);
return false;
}
}
return true;
}
}
module.exports.Validator = Validator;
================================================
FILE: lib/chat/server/messages-guard.js
================================================
/**
*
* Reldens - MessagesGuard
*
* Validates chat messages before processing them.
*
*/
const { GameConst } = require('../../game/constants');
const { ChatConst } = require('../constants');
const { sc } = require('@reldens/utils');
class MessagesGuard
{
/**
* @param {Object} message
* @returns {boolean}
*/
static validate(message)
{
if(ChatConst.CHAT_ACTION !== sc.get(message, GameConst.ACTION_KEY, '')){
return false;
}
return 0 !== sc.get(message, ChatConst.MESSAGE.KEY, '').trim().replace('#', '').replace('@', '').length;
}
}
module.exports.MessagesGuard = MessagesGuard;
================================================
FILE: lib/chat/server/plugin.js
================================================
/**
*
* Reldens - ChatPlugin
*
* Initializes and manages the chat system on the server side including rooms and event listeners.
*
*/
const { RoomChat } = require('./room-chat');
const { ChatMessageActions } = require('./message-actions');
const { ChatManager } = require('./manager');
const { PlayerSkills } = require('./event-listener/player-skills');
const { NpcSkills } = require('./event-listener/npc-skills');
const { GuestInvalidChangePoint } = require('./event-listener/guest-invalid-change-point');
const { PluginInterface } = require('../../features/plugin-interface');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManagerSingleton} EventsManagerSingleton
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
*/
class ChatPlugin extends PluginInterface
{
/**
* @param {Object} props
* @param {EventsManagerSingleton} [props.events]
* @param {BaseDataServer} [props.dataServer]
*/
async setup(props)
{
/** @type {EventsManagerSingleton|boolean} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in ChatPlugin.');
}
/** @type {BaseDataServer|boolean} */
this.dataServer = sc.get(props, 'dataServer', false);
if(!this.dataServer){
Logger.error('DataServer undefined in ChatPlugin.');
}
/** @type {Object|boolean} */
this.chatConfig = false;
/** @type {ChatManager} */
this.chatManager = new ChatManager({dataServer: this.dataServer});
/** @type {Array} */
this.rooms = ['chat'];
/** @type {GuestInvalidChangePoint} */
this.guestEventsListener = new GuestInvalidChangePoint();
this.listenEvents();
}
/**
* @returns {boolean}
*/
listenEvents()
{
if(!this.events){
return false;
}
this.events.on('reldens.beforeSuperInitialGameData', async (superInitialGameData) => {
superInitialGameData.chatTypes = await this.dataServer.getEntity('chatMessageTypes').loadAll();
});
this.events.on('reldens.roomsDefinition', (roomsList) => {
// here we are adding the chat room to be defined in the game server:
roomsList.push({roomName: 'chat', room: RoomChat});
});
this.events.on('reldens.serverConfigFeaturesReady', (props) => {
this.chatConfig = props.configProcessor.get('client/ui/chat');
});
// when the client sent a message to any room it will be checked by all the global messages defined:
this.events.on('reldens.roomsMessageActionsGlobal', (roomMessageActions) => {
roomMessageActions.chat = new ChatMessageActions({dataServer: this.dataServer});
});
this.events.on('reldens.actionsPrepareEventsListeners', async (actionsPack, classPath) => {
PlayerSkills.listenEvents(classPath, this.chatConfig, this.chatManager);
});
this.events.on('reldens.setupActions', async (props) => {
NpcSkills.listenEvents(props, this.chatConfig, this.chatManager);
});
this.events.on('reldens.guestInvalidChangePoint', async (event) => {
await this.guestEventsListener.sendMessage(event, this.chatManager);
});
}
}
module.exports.ChatPlugin = ChatPlugin;
================================================
FILE: lib/chat/server/room-chat.js
================================================
/**
*
* Reldens - RoomChat
*
* Handles the global chat room for private and global messages.
*
*/
const { RoomLogin } = require('../../rooms/server/login');
const { ChatManager } = require('./manager');
const { MessageFactory } = require('../message-factory');
const { Cleaner } = require('../cleaner');
const { ChatConst } = require('../constants');
const { GameConst } = require('../../game/constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@colyseus/core').Client} ColyseusClient
*/
class RoomChat extends RoomLogin
{
/**
* @param {Object} props
*/
onCreate(props)
{
super.onCreate(props);
Logger.info('Created RoomChat: '+this.roomName+' ('+this.roomId+').');
this.roomType = ChatConst.ROOM_TYPE_CHAT;
let dataServer = sc.get(this, 'dataServer', false);
if(!dataServer){
Logger.error('DataServer undefined in RoomChat.');
}
this.chatManager = new ChatManager({dataServer: this.dataServer});
delete props.roomsManager.creatingInstances[this.roomName];
}
/**
* @param {ColyseusClient} client
* @param {Object} props
* @param {Object} userModel
*/
onJoin(client, props, userModel)
{
this.loginManager.activePlayers.add(userModel, client, this);
}
/**
* @param {ColyseusClient} client
* @param {Object} data
* @returns {Promise}
*/
async handleReceivedMessage(client, data)
{
if(data[GameConst.ACTION_KEY] !== ChatConst.CHAT_ACTION){
return;
}
let text = Cleaner.cleanMessage(
data[ChatConst.MESSAGE.KEY],
this.config.get('client/chat/messages/characterLimit')
);
if(
0 === text.replace('#', '').trim().length
// do not count the player name on private messages:
|| (-1 !== text.indexOf('@') && 0 === text.substring(text.indexOf(' ')).trim().length)
){
// do nothing if text is shorter than 3 characters (including @ and #):
return;
}
let activePlayer = this.activePlayerBySessionId(client.sessionId, this.roomId);
if(!activePlayer){
Logger.warning('Current Active Player not found: '+client.sessionId);
return;
}
if(0 === text.indexOf('@')){
return await this.sendPrivateMessage(client, data[ChatConst.CHAT_TO], text, activePlayer);
}
if(0 === text.indexOf('#')){
return await this.sendGlobalMessage(client, text, activePlayer);
}
}
/**
* @param {ColyseusClient} client
* @param {string} toPlayer
* @param {string} text
* @param {Object} activePlayer
* @returns {Promise}
*/
async sendPrivateMessage(client, toPlayer, text, activePlayer)
{
if(!toPlayer){
Logger.info('Missing player recipient.');
return false;
}
let activePlayerTo = this.activePlayerByPlayerName(toPlayer, this.roomId);
if(!activePlayerTo){
let message = ChatConst.SNIPPETS.PRIVATE_MESSAGE_PLAYER_NOT_FOUND;
let messageData = {
[ChatConst.MESSAGE.DATA.PLAYER_NAME]: toPlayer
};
let messageObject = MessageFactory.create(
ChatConst.TYPES.ERROR,
message,
messageData
);
client.send('*', messageObject);
let saveResult = await this.chatManager.saveMessage(
MessageFactory.withDataToJson(message, messageData),
activePlayer.playerId,
activePlayer?.playerData?.state?.room_id,
activePlayerTo?.playerData,
ChatConst.TYPES.ERROR
);
if(!saveResult){
Logger.critical('Private failed chat save error.', messageObject);
}
return;
}
let messageObject = MessageFactory.create(
ChatConst.TYPES.PRIVATE,
text.substring(text.indexOf(' ')),
{},
activePlayer.playerName
);
client.send('*', messageObject);
activePlayerTo?.client.send('*', messageObject);
let saveResult = await this.chatManager.saveMessage(
messageObject[ChatConst.MESSAGE.KEY],
activePlayer.playerId,
activePlayer?.playerData?.state?.room_id,
activePlayerTo?.playerData,
ChatConst.TYPES.PRIVATE
);
if(!saveResult){
Logger.critical('Private chat save error.', messageObject);
}
}
/**
* @param {ColyseusClient} client
* @param {string} text
* @param {Object} activePlayer
* @returns {Promise}
*/
async sendGlobalMessage(client, text, activePlayer)
{
if(!this.config.get('server/chat/messages/global_enabled')){
return client.send('*', MessageFactory.create(
ChatConst.TYPES.ERROR,
ChatConst.SNIPPETS.GLOBAL_MESSAGE_NOT_ALLOWED
));
}
let globalAllowedRoles = this.config.get('server/chat/messages/global_allowed_roles').split(',').map(Number);
if(-1 === globalAllowedRoles.indexOf(activePlayer.roleId)){
return client.send('*', MessageFactory.create(
ChatConst.TYPES.ERROR,
ChatConst.SNIPPETS.GLOBAL_MESSAGE_PERMISSION_DENIED,
));
}
let messageObject = MessageFactory.create(
ChatConst.TYPES.GLOBAL,
text.substring(1),
{},
activePlayer.playerName
);
this.broadcast('*', messageObject);
let saveResult = await this.chatManager.saveMessage(
messageObject[ChatConst.MESSAGE.KEY],
activePlayer.playerId,
activePlayer?.playerData?.state?.room_id,
false,
ChatConst.TYPES.GLOBAL
);
if(!saveResult){
Logger.critical('Global chat save error.', messageObject);
}
}
/**
* @param {ColyseusClient} client
* @param {boolean} consented
* @returns {Promise}
*/
async onLeave(client, consented)
{
this.broadcastLeaveMessage(client.sessionId);
this.loginManager.activePlayers.removeByRoomAndSessionId(client.sessionId, this.roomId);
}
/**
* @param {string} sessionId
* @returns {boolean}
*/
broadcastLeaveMessage(sessionId)
{
let activePlayer = this.activePlayerBySessionId(sessionId, this.roomId);
if(!activePlayer){
return false;
}
if(!this.config.getWithoutLogs('server/chat/messages/broadcast_leave', false)){
return false;
}
let message = ChatConst.SNIPPETS.LEFT_ROOM;
let messageData = {
[ChatConst.MESSAGE.DATA.PLAYER_NAME]: activePlayer.playerName
};
let messageObject = MessageFactory.create(
ChatConst.TYPES.SYSTEM,
message,
messageData
);
this.broadcast('*', messageObject);
}
}
module.exports.RoomChat = RoomChat;
================================================
FILE: lib/config/client/config-manager.js
================================================
/**
*
* Reldens - ConfigManager
*
* Client-side configuration manager extending ConfigProcessor.
*
*/
const { ConfigProcessor } = require('../processor');
class ConfigManager extends ConfigProcessor
{
constructor()
{
super();
/** @type {Object} */
this.client = {
customClasses: {
message: {
listeners: {}
}
},
message: {
listeners: {}
}
};
}
}
module.exports.ConfigManager = ConfigManager;
================================================
FILE: lib/config/constants.js
================================================
/**
*
* Reldens - ConfigConst
*
*/
module.exports.ConfigConst = {
CONFIG_TYPE_TEXT: 1,
CONFIG_TYPE_FLOAT: 2,
CONFIG_TYPE_BOOLEAN: 3,
CONFIG_TYPE_JSON: 4,
CONFIG_TYPE_COMMA_SEPARATED: 5
};
================================================
FILE: lib/config/processor.js
================================================
/**
*
* Reldens - ConfigProcessor
*
* Base configuration processor with path-based config retrieval.
*
*/
const { Logger, sc } = require('@reldens/utils');
class ConfigProcessor
{
constructor()
{
/** @type {boolean} */
this.avoidLog = false;
}
/**
* @param {string} path
* @param {any} [defaultValue]
* @returns {any}
*/
get(path, defaultValue)
{
let defaultReturn = 'undefined' !== typeof defaultValue ? defaultValue : false;
let pathArray = path.split('/');
if(2 > pathArray.length){
if(!this.avoidLog){
Logger.error('Path level is too low:', path);
}
return defaultReturn;
}
let levelCheck = (this[pathArray[0]] || {});
for(let i = 1; i < pathArray.length; i++){
if(!sc.hasOwn(levelCheck, pathArray[i])){
if(!this.avoidLog){
Logger.error('Configuration level '+i+' > "'+pathArray[i]+'" not defined: '+path);
}
levelCheck = defaultReturn;
break;
}
levelCheck = levelCheck[pathArray[i]];
}
return levelCheck;
}
/**
* @param {string} path
* @param {any} [defaultValue]
* @returns {any}
*/
getWithoutLogs(path, defaultValue = false)
{
this.avoidLog = true;
let result = this.get(path, defaultValue);
this.avoidLog = false;
return result;
}
}
module.exports.ConfigProcessor = ConfigProcessor;
================================================
FILE: lib/config/server/entities/config-entity-override.js
================================================
/**
*
* Reldens - ConfigEntityOverride
*
* Extends config entity with custom navigation position and sorting for admin panel.
*
*/
const { ConfigEntity } = require('../../../../generated-entities/entities/config-entity');
class ConfigEntityOverride extends ConfigEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.navigationPosition = 2000;
config.sort = {sortBy: 'path'};
return config;
}
}
module.exports.ConfigEntityOverride = ConfigEntityOverride;
================================================
FILE: lib/config/server/entities/config-types-entity-override.js
================================================
/**
*
* Reldens - ConfigTypesEntityOverride
*
* Extends config types entity with custom navigation position for admin panel.
*
*/
const { ConfigTypesEntity } = require('../../../../generated-entities/entities/config-types-entity');
class ConfigTypesEntityOverride extends ConfigTypesEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.navigationPosition = 2010;
return config;
}
}
module.exports.ConfigTypesEntityOverride = ConfigTypesEntityOverride;
================================================
FILE: lib/config/server/entities-config.js
================================================
/**
*
* Reldens - Entities Config
*
*/
const { ConfigEntityOverride } = require('./entities/config-entity-override');
const { ConfigTypesEntityOverride } = require('./entities/config-types-entity-override');
module.exports.entitiesConfig = {
config: ConfigEntityOverride,
configTypes: ConfigTypesEntityOverride,
};
================================================
FILE: lib/config/server/entities-translations.js
================================================
/**
*
* Reldens - Entities Translations
*
*/
module.exports.entitiesTranslations = {
labels: {
settings: 'Settings',
config: 'Config',
config_types: 'Config Types'
}
};
================================================
FILE: lib/config/server/manager.js
================================================
/**
*
* Reldens - ConfigManager
*
* Manages configurations from a database and includes default values from config files.
* Loads server and client configurations, processes them by type, and makes them available
* throughout the application.
*
*/
const { ConfigProcessor } = require('../processor');
const { ConfigConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
const PackageData = require('../../../package.json');
/**
* @typedef {Object} ConfigManagerProps
* @property {EventsManager} [events] - Events manager instance from @reldens/utils
* @property {BaseDataServer} [dataServer] - Data server instance from @reldens/storage
* @property {Object} [customClasses] - Custom class overrides map
*/
class ConfigManager extends ConfigProcessor
{
/**
* @param {ConfigManagerProps} props
*/
constructor(props)
{
super();
/** @type {EventsManager|false} - Events manager for pub/sub */
this.events = sc.get(props, 'events', false);
/** @type {BaseDataServer|false} - Data server for database access */
this.dataServer = sc.get(props, 'dataServer', false);
/** @type {{server: Object, client: Object}} - Configuration storage */
this.configList = {
server: {},
client: {}
};
this.configList.server.customClasses = sc.get(props, 'customClasses', {});
}
/**
* @returns {Promise}
*/
async loadConfigurations()
{
if(!this.events){
Logger.error('EventsManager undefined in ConfigManager.');
return false;
}
if(!this.dataServer){
Logger.error('Data Server undefined in ConfigManager.');
return false;
}
this.configList.client.gameEngine = {version: PackageData.version};
await this.events.emit('reldens.beforeLoadConfigurations', {configManager: this});
let configCollection = await this.dataServer.getEntity('config').loadAll();
for(let config of configCollection){
// create an object for each scope:
if(!sc.hasOwn(this.configList, config.scope)){
this.configList[config.scope] = {};
}
let pathSplit = config.path.split('/');
// path must have at least 2 parts:
if(2 > pathSplit.length){
Logger.error('Invalid configuration:', config);
continue;
}
let parsedValue = await this.getParsedValue(config);
this.loopObjectAndAssignProperty(this.configList[config.scope], pathSplit, parsedValue);
}
Object.assign(this, this.configList);
}
/**
* @param {Object} configList
* @param {Array} pathSplit
* @param {any} parsedValue
* @returns {void}
*/
loopObjectAndAssignProperty(configList, pathSplit, parsedValue)
{
let idx = pathSplit[0];
if(!sc.hasOwn(configList, idx)){
configList[idx] = 1 < pathSplit.length ? {} : parsedValue;
}
if(1 < pathSplit.length){
this.loopObjectAndAssignProperty(configList[idx], pathSplit.slice(1, pathSplit.length), parsedValue);
}
}
/**
* Since everything coming from the database is a string then we parse the config type to return the value in the
* proper type.
*
* @param {Object} config
* @returns {Promise}
*/
async getParsedValue(config)
{
await this.events.emit('reldens.beforeGetParsedValue', {configManager: this, config: config});
if(config.type === ConfigConst.CONFIG_TYPE_TEXT){
return config.value.toString();
}
if(config.type === ConfigConst.CONFIG_TYPE_BOOLEAN){
return !(config.value === 'false' || config.value === '0');
}
if(config.type === ConfigConst.CONFIG_TYPE_FLOAT){
return parseFloat(config.value);
}
if(config.type === ConfigConst.CONFIG_TYPE_JSON){
try {
return sc.toJson(config.value);
} catch (e) {
Logger.error('Invalid JSON on configuration:', config);
}
}
if(config.type === ConfigConst.CONFIG_TYPE_COMMA_SEPARATED){
return config.value.split(',');
}
return config.value;
}
}
module.exports.ConfigManager = ConfigManager;
================================================
FILE: lib/features/client/config-client.js
================================================
/**
*
* Reldens - Client Core Features
*
* All the core features plugins will be available here.
* Later we can control if the feature is enabled/disabled using the configuration in the storage.
* Core features will be available as part of the current Reldens version.
*
*/
const { ChatPlugin } = require('../../chat/client/plugin');
const { ObjectsPlugin } = require('../../objects/client/plugin');
const { InventoryPlugin } = require('../../inventory/client/plugin');
const { ActionsPlugin } = require('../../actions/client/plugin');
const { UsersPlugin } = require('../../users/client/plugin');
const { AudioPlugin } = require('../../audio/client/plugin');
const { RoomsPlugin } = require('../../rooms/client/plugin');
const { PredictionPlugin } = require('../../prediction/client/plugin');
const { TeamsPlugin } = require('../../teams/client/plugin');
const { SnippetsPlugin } = require('../../snippets/client/plugin');
const { AdsPlugin } = require('../../ads/client/plugin');
const { WorldPlugin } = require('../../world/client/plugin');
const { ScoresPlugin } = require('../../scores/client/plugin');
const { RewardsPlugin } = require('../../rewards/client/plugin');
module.exports.ClientCoreFeatures = {
chat: ChatPlugin,
objects: ObjectsPlugin,
inventory: InventoryPlugin,
actions: ActionsPlugin,
users: UsersPlugin,
audio: AudioPlugin,
rooms: RoomsPlugin,
prediction: PredictionPlugin,
teams: TeamsPlugin,
snippets: SnippetsPlugin,
ads: AdsPlugin,
world: WorldPlugin,
scores: ScoresPlugin,
rewards: RewardsPlugin
};
================================================
FILE: lib/features/client/manager.js
================================================
/**
*
* Reldens - FeaturesManager
*
* This class will handle features activation on the client depending on the configuration received from the server.
*
*/
const { ClientCoreFeatures } = require('./config-client');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../game/client/game-manager').GameManager} GameManager
* @typedef {import('@reldens/utils').EventsManager} EventsManager
*/
class FeaturesManager
{
/**
* @param {Object} props
*/
constructor(props)
{
/** @type {GameManager|boolean} */
this.gameManager = sc.get(props, 'gameManager', false);
/** @type {EventsManager|boolean} */
this.events = sc.get(props, 'events', false);
/** @type {Object} */
this.featuresList = {};
}
/**
* @param {Object} featuresCodeList
* @returns {Promise}
*/
async loadFeatures(featuresCodeList)
{
if(!this.gameManager){
Logger.error('Game Manager undefined in FeaturesManager.');
return false;
}
if(!this.events){
Logger.error('EventsManager undefined in FeaturesManager.');
return false;
}
await this.events.emit('reldens.loadFeatures', this, featuresCodeList);
let featuresKeys = Object.keys(featuresCodeList);
if(0 === featuresKeys.length){
return this.featuresList;
}
for(let i of featuresKeys){
let featureCode = featuresCodeList[i];
if(!sc.hasOwn(ClientCoreFeatures, featureCode)){
continue;
}
this.featuresList[featureCode] = new ClientCoreFeatures[featureCode]();
if('function' === typeof this.featuresList[featureCode].setup){
await this.featuresList[featureCode].setup({gameManager: this.gameManager, events: this.events});
}
await this.events.emit('reldens.loadFeature_'+featureCode, this.featuresList[featureCode], this);
}
return this.featuresList;
}
}
module.exports.FeaturesManager = FeaturesManager;
================================================
FILE: lib/features/plugin-interface.js
================================================
/**
*
* Reldens - Plugin Interface
*
* Base interface for client and server feature plugins.
*
*/
const { Logger } = require('@reldens/utils');
class PluginInterface
{
/**
* @param {Object} props
* @param {Array} [props.requiredProperties]
* @param {Object} [props.events]
* @param {Object} [props.dataServer]
* @param {Object} [props.config]
* @param {Object} [props.featuresManager]
* @param {Object} [props.themeManager]
* @returns {Promise}
*/
async setup(props)
{
Logger.error('Setup plugin not implemented.', props);
return false;
}
}
module.exports.PluginInterface = PluginInterface;
================================================
FILE: lib/features/server/config-server.js
================================================
/**
*
* Reldens - Server Core Features
*
* All the core features plugins will be available here.
* Later we can control if the feature is enabled/disabled using the configuration in the storage.
* Core features will be available as part of the current Reldens version.
*
*/
const { ActionsPlugin } = require('../../actions/server/plugin');
const { ChatPlugin } = require('../../chat/server/plugin');
const { RespawnPlugin } = require('../../respawn/server/plugin');
const { InventoryPlugin } = require('../../inventory/server/plugin');
const { FirebasePlugin } = require('../../firebase/server/plugin');
const { UsersPlugin } = require('../../users/server/plugin');
const { AudioPlugin } = require('../../audio/server/plugin');
const { RoomsPlugin } = require('../../rooms/server/plugin');
const { AdminPlugin } = require('../../admin/server/plugin');
const { TeamsPlugin } = require('../../teams/server/plugin');
const { RewardsPlugin } = require('../../rewards/server/plugin');
const { SnippetsPlugin } = require('../../snippets/server/plugin');
const { ObjectsPlugin } = require('../../objects/server/plugin');
const { AdsPlugin } = require('../../ads/server/plugin');
const { ScoresPlugin } = require('../../scores/server/plugin');
module.exports.ServerCoreFeatures = {
chat: ChatPlugin,
respawn: RespawnPlugin,
inventory: InventoryPlugin,
firebase: FirebasePlugin,
actions: ActionsPlugin,
users: UsersPlugin,
audio: AudioPlugin,
rooms: RoomsPlugin,
admin: AdminPlugin,
teams: TeamsPlugin,
rewards: RewardsPlugin,
snippets: SnippetsPlugin,
objects: ObjectsPlugin,
ads: AdsPlugin,
scores: ScoresPlugin
};
================================================
FILE: lib/features/server/entities/features-entity-override.js
================================================
/**
*
* Reldens - FeaturesEntityOverride
*
* Extend features entity with customized list properties for admin panel.
*
*/
const { FeaturesEntity } = require('../../../../generated-entities/entities/features-entity');
class FeaturesEntityOverride extends FeaturesEntity
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let config = super.propertiesConfig(extraProps);
config.navigationPosition = 1400;
config.listProperties.splice(config.listProperties.indexOf('code'), 1);
return config;
}
}
module.exports.FeaturesEntityOverride = FeaturesEntityOverride;
================================================
FILE: lib/features/server/entities/features-entity.js
================================================
/**
*
* Reldens - FeaturesEntity
*
* Entity configuration for features table defining admin panel properties.
*
*/
const { EntityProperties } = require('../../../game/server/entity-properties');
class FeaturesEntity extends EntityProperties
{
/**
* @param {Object} extraProps
* @returns {Object}
*/
static propertiesConfig(extraProps)
{
let titleProperty = 'title';
let properties = {
id: {},
code: {
isRequired: true
},
[titleProperty]: {
isRequired: true
},
is_enabled: {
type: 'boolean',
isRequired: true
}
};
let showProperties = Object.keys(properties);
let listProperties = [...showProperties];
let editProperties = [...showProperties];
listProperties.splice(listProperties.indexOf('code'), 1);
editProperties.splice(editProperties.indexOf('id'), 1);
return {
showProperties,
editProperties,
listProperties,
filterProperties: listProperties,
properties,
titleProperty,
...extraProps,
navigationPosition: 1400
};
}
}
module.exports.FeaturesEntity = FeaturesEntity;
================================================
FILE: lib/features/server/entities-config.js
================================================
/**
*
* Reldens - Entities Config
*
*/
const { FeaturesEntityOverride } = require('./entities/features-entity-override');
module.exports.entitiesConfig = {
features: FeaturesEntityOverride
};
================================================
FILE: lib/features/server/entities-translations.js
================================================
/**
*
* Reldens - Entities Translations
*
*/
module.exports.entitiesTranslations = {
labels: {
features: 'Features'
}
};
================================================
FILE: lib/features/server/manager.js
================================================
/**
*
* Reldens - FeaturesManager
*
* Server-side features manager that loads enabled features from database.
*
*/
const { SetupServerProperties } = require('./setup-server-properties');
const { ServerCoreFeatures } = require('./config-server');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
* @typedef {import('../game/server/config-manager').ConfigManager} ConfigManager
* @typedef {import('../game/server/theme-manager').ThemeManager} ThemeManager
*/
class FeaturesManager
{
/**
* @param {Object} props
*/
constructor(props)
{
/** @type {Object} */
this.availableFeatures = ServerCoreFeatures;
/** @type {Object} */
this.featuresList = {};
/** @type {Array} */
this.featuresCodeList = [];
/** @type {EventsManager|boolean} */
this.events = sc.get(props, 'events', false);
/** @type {BaseDataServer|boolean} */
this.dataServer = sc.get(props, 'dataServer', false);
/** @type {ThemeManager|boolean} */
this.themeManager = sc.get(props, 'themeManager', false);
/** @type {ConfigManager} */
this.config = sc.get(props, 'config', {});
}
/**
* @returns {Promise|boolean>}
*/
async loadFeatures()
{
if(!this.events){
Logger.error('EventsManager undefined in FeaturesManager.');
return false;
}
if(!this.dataServer){
Logger.error('DataServer undefined in FeaturesManager.');
return false;
}
let featuresCollection = await this.dataServer.getEntity('features').loadBy('is_enabled', 1);
let setupServerProperties = new SetupServerProperties({
events: this.events,
dataServer: this.dataServer,
config: this.config,
themeManager: this.themeManager,
featuresManager: this
});
if(!setupServerProperties.validate()){
return false;
}
for(let featureEntity of featuresCollection){
this.featuresCodeList.push(featureEntity.code);
// @NOTE: featuresCodeList this will be sent to the client, so we need the complete list from the database
// to load the features for the client side later.
if(
// only include enabled and available features on the server side config:
sc.hasOwn(this.availableFeatures, featureEntity.code)
&& sc.hasOwn(featureEntity, 'is_enabled')
&& featureEntity.is_enabled
){
// get feature package server for server side:
let featurePackage = this.availableFeatures[featureEntity.code];
// set package on entity:
featureEntity.package = new featurePackage();
if('function' === typeof featureEntity.package.setup){
await featureEntity.package.setup(setupServerProperties);
}
// for last add the feature entity to the list:
this.featuresList[featureEntity.code] = featureEntity;
Logger.info('Enabled feature: ' + featureEntity.code);
}
}
this.events.emit('reldens.featuresManagerLoadFeaturesAfter', {featuresManager: this, featuresCollection});
// return the features code list:
return this.featuresCodeList;
}
}
module.exports.FeaturesManager = FeaturesManager;
================================================
FILE: lib/features/server/setup-server-properties.js
================================================
/**
*
* Reldens - SetupServerProperties
*
* Properties container for server feature plugin setup.
*
*/
const { PropertiesHandler } = require('../../game/properties-handler');
const { sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer
* @typedef {import('../../game/server/config-manager').ConfigManager} ConfigManager
* @typedef {import('./manager').FeaturesManager} FeaturesManager
* @typedef {import('../../game/server/theme-manager').ThemeManager} ThemeManager
*/
class SetupServerProperties extends PropertiesHandler
{
/**
* @param {Object} props
*/
constructor(props)
{
super();
/** @type {EventsManager|boolean} */
this.events = sc.get(props, 'events', false);
/** @type {BaseDataServer|boolean} */
this.dataServer = sc.get(props, 'dataServer', false);
/** @type {ConfigManager} */
this.config = sc.get(props, 'config', {});
/** @type {FeaturesManager|boolean} */
this.featuresManager = sc.get(props, 'featuresManager', false);
/** @type {ThemeManager|boolean} */
this.themeManager = sc.get(props, 'themeManager', false);
/** @type {Array} */
this.requiredProperties = Object.keys(this);
}
}
module.exports.SetupServerProperties = SetupServerProperties
================================================
FILE: lib/firebase/client/connector.js
================================================
/**
*
* Reldens - FirebaseConnector
*
* Handles Firebase authentication integration on the client-side. Manages authentication providers
* (Google, Facebook, GitHub), configures Firebase SDK, handles sign-in flows with popup authentication,
* and coordinates with the game's login system. Integrates with GameManager and GameDom for UI updates.
*
*/
const FirebaseApp = require('firebase/compat/app').default;
const FirebaseAnalytics = require('firebase/compat/analytics');
const FirebaseAuth = require('firebase/compat/auth');
const { ErrorsBlockHandler } = require('../../game/client/handlers/errors-block-handler');
const { GameConst } = require('../../game/constants');
const { ErrorManager, Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
* @typedef {import('../../game/client/game-dom').GameDom} GameDom
* @typedef {import('firebase/compat/app').default} FirebaseAppModule
*
* @typedef {Object} FirebaseProvider
* @property {string} label
* @property {Object} authMethod
*/
class FirebaseConnector
{
/**
* @param {GameManager} gameManager
*/
constructor(gameManager)
{
if(!gameManager){
ErrorManager.error('FirebaseConnector - Missing game manager.');
}
/** @type {GameManager} */
this.gameManager = gameManager;
/** @type {GameDom} */
this.gameDom = this.gameManager.gameDom;
/** @type {typeof FirebaseAnalytics} */
this.analytics = FirebaseAnalytics;
/** @type {typeof FirebaseApp} */
this.app = FirebaseApp;
/** @type {typeof FirebaseAuth} */
this.auth = FirebaseAuth;
/** @type {Object|false} */
this.initializedApp = false;
/** @type {boolean} */
this.isActive = false;
/** @type {string} */
this.containerId = '#firebase-auth-container';
/** @type {Object} */
this.activeProviders = {};
/** @type {Object} */
this.defaultProviders = {};
this.gameManager.events.on('reldens.beforeJoinGame', (props) => {
if(props.formData['formId'] === 'firebase-login'){
props.gameManager.userData.isFirebaseLogin = true;
}
});
}
/**
* @returns {Object}
*/
fetchDefaultProviders()
{
return {
google: {
label: 'Sign in with Google',
authMethod: new this.app.auth.GoogleAuthProvider()
},
facebook: {
label: 'Sign in with Facebook',
authMethod: new this.app.auth.FacebookAuthProvider()
},
github: {
label: 'Sign in with GitHub',
authMethod: new this.app.auth.GithubAuthProvider()
}
};
}
startFirebase()
{
let firebaseUrl = this.gameManager.appServerUrl+GameConst.ROUTE_PATHS.FIREBASE;
this.gameDom.getJSON(firebaseUrl, (err, response) => {
if(!response.enabled){
return false;
}
let firebaseConfig = response.firebaseConfig;
this.initAuth(firebaseConfig);
// logout on refresh:
this.gameDom.getWindow().addEventListener('beforeunload', () => {
if(this.isActive){
this.initializedApp.firebase.auth().signOut();
}
});
// check the current auth state:
this.initializedApp.firebase.auth().onAuthStateChanged((user) => {
if(user){
this.setActiveUser(user);
return false;
}
this.setupAuthButtons(response.providersKeys);
return false;
});
let firebaseLogin = this.gameDom.getElement('#firebase-login');
if(firebaseLogin){
this.activateLoginBehavior(firebaseLogin);
}
});
}
/**
* @param {HTMLFormElement} firebaseLogin
*/
activateLoginBehavior(firebaseLogin)
{
firebaseLogin.addEventListener('submit', (e) => {
e.preventDefault();
if(!firebaseLogin.checkValidity()){
return false;
}
this.gameDom.getElement('.firebase-row-container').classList.remove('hidden');
});
let firebaseUser = this.gameDom.getElement('#firebase-username');
if(!firebaseUser){
return false;
}
this.gameDom.getElement('.firebase-row-container').classList.remove('hidden');
firebaseUser.addEventListener('change', () => {
ErrorsBlockHandler.reset(firebaseLogin);
});
firebaseUser.addEventListener('focus', () => {
ErrorsBlockHandler.reset(firebaseLogin);
});
}
/**
* @param {Array} providersKeys
*/
setupAuthButtons(providersKeys)
{
this.isActive = false;
let container = this.gameDom.getElement(this.containerId);
if(!container){
return false;
}
container.innerHTML = '';
if(0 === providersKeys.length){
return false;
}
this.defaultProviders = this.fetchDefaultProviders();
for(let providerKey of providersKeys){
let provider = sc.get(this.activeProviders, providerKey, false);
if(!provider){
provider = this.defaultProviders[providerKey];
}
if(!provider){
return false;
}
let authButton = this.createAuthButton(providerKey, provider.label);
authButton.addEventListener('click', () => {
this.signInWithProvider(provider.authMethod);
});
container.appendChild(authButton);
}
}
/**
* @param {string} provider
* @param {string} text
* @returns {HTMLButtonElement}
*/
createAuthButton(provider, text)
{
let button = document.createElement('button');
button.type = 'button';
button.className = 'firebase-auth-btn firebase-' + provider + '-btn';
button.innerHTML = text;
return button;
}
/**
* @param {Object} providerAuthMethod
*/
signInWithProvider(providerAuthMethod)
{
if(!providerAuthMethod){
return false;
}
this.initializedApp.firebase.auth().signInWithPopup(providerAuthMethod).catch((error) => {
Logger.error('Firebase authentication error:', error);
let errorContainer = this.gameDom.getElement('#firebase-login .response-error');
if(errorContainer){
errorContainer.textContent = 'Authentication error.';
}
});
}
/**
* @param {Object} user
*/
setActiveUser(user)
{
this.isActive = true;
let usernameInput = this.gameDom.getElement('#firebase-username');
if(!usernameInput || '' === usernameInput.value.trim()){
let errorContainer = this.gameDom.getElement('#firebase-login .response-error');
if(errorContainer){
errorContainer.textContent = 'Please enter a username.';
}
return false;
}
let formData = {
formId: 'firebase-login',
email: user.email,
username: usernameInput.value,
password: user.uid
};
this.gameManager.startGame(formData, true);
}
/**
* @param {Object} firebaseConfig
*/
initAuth(firebaseConfig)
{
if(!firebaseConfig){
Logger.error('Missing firebase configuration.');
return false;
}
this.firebaseConfig = firebaseConfig;
this.initializedApp = this.app.initializeApp(this.firebaseConfig);
if(sc.hasOwn(this.firebaseConfig, 'measurementId')){
this.initializedApp.firebase.analytics();
}
}
}
module.exports.FirebaseConnector = FirebaseConnector;
================================================
FILE: lib/firebase/server/plugin.js
================================================
/**
*
* Reldens - Firebase Server Plugin
*
* Server-side plugin that integrates Firebase authentication with Reldens. Loads Firebase configuration
* from the config manager or environment variables, exposes a configuration endpoint for the client,
* and manages authentication provider settings (Google, Facebook, GitHub).
*
*/
const { PluginInterface } = require('../../features/plugin-interface');
const { GameConst } = require('../../game/constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('../../game/server/config-manager').ConfigManager} ConfigManager
* @typedef {import('express').Application} ExpressApplication
*
* @typedef {Object} FirebasePluginProps
* @property {EventsManager} events
* @property {ConfigManager} config
*/
class FirebasePlugin extends PluginInterface
{
/**
* @param {FirebasePluginProps} props
* @returns {Promise}
*/
async setup(props)
{
/** @type {EventsManager|boolean} */
this.events = sc.get(props, 'events', false);
/** @type {ConfigManager|boolean} */
this.config = sc.get(props, 'config', false);
this.listenEvents();
this.mapConfiguration();
}
listenEvents() {
if (!this.events) {
Logger.error('EventsManager undefined in FirebasePlugin.');
return false;
}
this.events.on('reldens.serverBeforeListen', (props) => {
this.declareFirebaseConfigRequestHandler(props.serverManager.app);
});
}
mapConfiguration()
{
if(!this.config){
Logger.error('Config undefined in FirebasePlugin.');
return false;
}
let env = process.env;
let config = 'server/firebase/';
/** @type {string} */
this.firebaseConfigRoute = this.config.getWithoutLogs(config+'configRoute', GameConst.ROUTE_PATHS.FIREBASE);
/** @type {boolean} */
this.isEnabled = this.config.getWithoutLogs(config+'enabled', 1 === Number(env.RELDENS_FIREBASE_ENABLE || 0));
/** @type {Array} */
this.providersKeys = this.config.getWithoutLogs(config+'providers', ['google', 'facebook', 'github']);
/** @type {Object} */
this.firebaseMappedConfig = {
apiKey: this.config.getWithoutLogs(config+'apiKey', env.RELDENS_FIREBASE_API_KEY),
authDomain: this.config.getWithoutLogs(config+'authDomain', env.RELDENS_FIREBASE_AUTH_DOMAIN),
databaseURL: this.config.getWithoutLogs(config+'databaseURL', env.RELDENS_FIREBASE_DATABASE_URL),
projectId: this.config.getWithoutLogs(config+'projectId', env.RELDENS_FIREBASE_PROJECT_ID),
storageBucket: this.config.getWithoutLogs(config+'storageBucket', env.RELDENS_FIREBASE_STORAGE_BUCKET),
messagingSenderId: this.config.getWithoutLogs(
config+'messagingSenderId',
env.RELDENS_FIREBASE_MESSAGING_SENDER_ID
),
appId: this.config.getWithoutLogs(config+'appId', env.RELDENS_FIREBASE_APP_ID)
};
let measurementId = this.config.getWithoutLogs(config+'measurementId', env.RELDENS_FIREBASE_MEASUREMENTID);
if (measurementId) {
this.firebaseMappedConfig['measurementId'] = measurementId;
}
}
/**
* @param {ExpressApplication} app
*/
declareFirebaseConfigRequestHandler(app)
{
app.get(this.firebaseConfigRoute, (req, res) => {
res.json(this.firebaseConfig());
});
}
/**
* @returns {Object}
*/
firebaseConfig()
{
if(!this.isEnabled){
return {enabled: false};
}
return {
enabled: true,
firebaseConfig: this.firebaseMappedConfig,
providersKeys: this.providersKeys
};
}
}
module.exports.FirebasePlugin = FirebasePlugin;
================================================
FILE: lib/game/allowed-file-types.js
================================================
/**
*
* Reldens - AllowedFileTypes
*
* Constants object defining the allowed file type categories for file upload validation and MIME type
* detection. Used by FileHandler and other utilities to categorize and validate file types (audio files,
* image files, text files) during uploads, asset processing, and file operations.
*
*/
module.exports.AllowedFileTypes = {
AUDIO: 'audio',
IMAGE: 'image',
TEXT: 'text'
};
================================================
FILE: lib/game/client/animations-defaults-merger.js
================================================
/**
*
* Reldens - AnimationsDefaultsMerger
*
*/
const { sc } = require('@reldens/utils');
/**
* @typedef {Object} RoomData
* @property {Object.} objectsAnimationsData
* @property {Object.} [animationsDefaults]
* @property {Object.} [preloadAssets]
* @property {Object.} [preloadAssetsDefaults]
*/
class AnimationsDefaultsMerger
{
/**
* @param {RoomData} roomData
* @returns {RoomData}
*/
static mergeDefaults(roomData)
{
if(!sc.hasOwn(roomData, 'animationsDefaults')){
return roomData;
}
if(!sc.hasOwn(roomData, 'objectsAnimationsData')){
return roomData;
}
let animationsDefaults = roomData.animationsDefaults;
let objectsAnimationsData = roomData.objectsAnimationsData;
let objectKeys = Object.keys(objectsAnimationsData);
for(let key of objectKeys){
let objectData = objectsAnimationsData[key];
if(!sc.hasOwn(objectData, 'asset_key')){
continue;
}
objectData.key = key;
let assetKey = objectData.asset_key;
if(!sc.hasOwn(animationsDefaults, assetKey)){
continue;
}
let defaults = animationsDefaults[assetKey];
objectsAnimationsData[key] = Object.assign({}, defaults, objectData);
}
delete roomData.animationsDefaults;
return roomData;
}
}
module.exports.AnimationsDefaultsMerger = AnimationsDefaultsMerger;
================================================
FILE: lib/game/client/communication/room-state-entities-manager.js
================================================
/**
*
* Reldens - RoomStateEntitiesManager
*
* Provides static methods for common room state entity patterns.
* Simplifies entity lifecycle management with automatic property listening.
* Browser-only code bundled by Parcel.
*
*/
const { StateCallbacksManager } = require('./state-callbacks-manager');
class RoomStateEntitiesManager
{
static onEntityAddWithProperties(room, collectionName, properties, onAddCallback, onPropertyChangeCallback)
{
let propertyCallbacks = {};
for(let prop of properties){
propertyCallbacks[prop] = (entity, key, value, previousValue) => {
if(onPropertyChangeCallback){
onPropertyChangeCallback(entity, key, prop, value, previousValue);
}
};
}
return this.onEntityAddWithPropertyCallbacks(room, collectionName, propertyCallbacks, onAddCallback);
}
static onEntityAddWithPropertyCallbacks(room, collectionName, propertyCallbacks, onAddCallback)
{
let manager = this.createManager(room);
let wrappedState = manager.wrap(room.state);
manager.onAdd(wrappedState[collectionName], (entity, key) => {
if(onAddCallback){
onAddCallback(entity, key);
}
for(let prop of Object.keys(propertyCallbacks)){
let callback = propertyCallbacks[prop];
manager.listen(entity, prop, (value, previousValue) => {
callback(entity, key, value, previousValue);
});
}
});
return manager;
}
static onEntityAdd(room, collectionName, callback)
{
return this._onCollectionEvent(room, collectionName, 'onAdd', callback);
}
static onEntityRemove(room, collectionName, callback)
{
return this._onCollectionEvent(room, collectionName, 'onRemove', callback);
}
static createManager(room)
{
return new StateCallbacksManager(room);
}
static _onCollectionEvent(room, collectionName, eventType, callback)
{
let manager = this.createManager(room);
let wrappedState = manager.wrap(room.state);
manager[eventType](wrappedState[collectionName], callback);
return manager;
}
}
module.exports.RoomStateEntitiesManager = RoomStateEntitiesManager;
================================================
FILE: lib/game/client/communication/state-callbacks-manager.js
================================================
/**
*
* Reldens - StateCallbacksManager
*
* Manages Colyseus 0.16 state callbacks with getStateCallbacks wrapper.
* Provides a unified API for state callback handling. Centralizes all state
* callback logic to minimize migration changes. Browser-only code bundled
* by Parcel.
*
*/
const { getStateCallbacks } = require('colyseus.js');
class StateCallbacksManager
{
constructor(room)
{
this.room = room;
this.$ = getStateCallbacks(room);
this.cleanupFunctions = [];
}
wrap(schemaObject)
{
return this.$(schemaObject);
}
onAdd(collection, callback)
{
let cleanup = collection.onAdd(callback);
if(cleanup){
this.cleanupFunctions.push(cleanup);
}
return cleanup;
}
onRemove(collection, callback)
{
let cleanup = collection.onRemove(callback);
if(cleanup){
this.cleanupFunctions.push(cleanup);
}
return cleanup;
}
onChange(collection, callback)
{
let cleanup = collection.onChange(callback);
if(cleanup){
this.cleanupFunctions.push(cleanup);
}
return cleanup;
}
listen(entity, propertyName, callback)
{
let wrappedEntity = this.wrap(entity);
let cleanup = wrappedEntity.listen(propertyName, callback);
if(cleanup){
this.cleanupFunctions.push(cleanup);
}
return cleanup;
}
listenAll(entity, properties, callback)
{
for(let prop of properties){
this.listen(entity, prop, (value, previousValue) => {
callback(prop, value, previousValue);
});
}
}
dispose()
{
for(let cleanup of this.cleanupFunctions){
if('function' === typeof cleanup){
cleanup();
}
}
this.cleanupFunctions = [];
}
}
module.exports.StateCallbacksManager = StateCallbacksManager;
================================================
FILE: lib/game/client/engine/sprite-text-factory.js
================================================
/**
*
* Reldens - SpriteTextFactory
*
* Factory class for creating and attaching text labels to sprites in Phaser. Provides static methods
* to position text relative to sprites (e.g., player names, labels) with configurable styling including
* font, colors, shadows, and strokes.
*
*/
const { sc } = require('@reldens/utils');
/**
* @typedef {import('phaser').GameObjects.Sprite} Sprite
* @typedef {import('phaser').Scene} Scene
* @typedef {import('phaser').GameObjects.Text} Text
*/
class SpriteTextFactory
{
/**
* @param {Sprite} sprite
* @param {string} text
* @param {Object} textConfig
* @param {number} topOff
* @param {string} textKeyWord
* @param {Scene} scene
* @returns {Text}
*/
static attachTextToSprite(sprite, text, textConfig, topOff, textKeyWord, scene)
{
let relativeNamePosition = this.getTextPosition(sprite, text, textConfig, topOff);
let textSprite = scene.add.text(
relativeNamePosition.x,
relativeNamePosition.y,
text,
{
fontFamily: sc.get(textConfig, 'fontFamily', 'sans-serif'),
fontSize: sc.get(textConfig, 'fontSize', '12px')
}
);
textSprite.style.setFill(sc.get(textConfig, 'fill', '#ffffff'));
textSprite.style.setAlign(sc.get(textConfig, 'align', 'center'));
textSprite.style.setStroke(sc.get(textConfig, 'stroke', '#000000'), sc.get(textConfig, 'strokeThickness', 4));
textSprite.style.setShadow(
sc.get(textConfig, 'shadowX', 5),
sc.get(textConfig, 'shadowY', 5),
sc.get(textConfig, 'shadowColor', 'rgba(0,0,0,0.7)'),
sc.get(textConfig, 'shadowBlur', 5)
);
textSprite.setDepth(sc.get(textConfig, 'depth', 200000));
sprite[textKeyWord] = textSprite;
return textSprite;
}
/**
* @param {Sprite} sprite
* @param {string} text
* @param {Object} textConfig
* @param {number} [topOff]
* @returns {Object} Position object with x and y coordinates
*/
static getTextPosition(sprite, text, textConfig, topOff = 0)
{
if(!sprite){
return {x: 0, y:0};
}
let height = sc.get(textConfig, 'height', 18);
let x = sprite.x - ((text.length * sc.get(textConfig, 'textLength', 4)));
let y = sprite.y - height - sprite.height + topOff;
return {x, y};
}
}
module.exports.SpriteTextFactory = SpriteTextFactory;
================================================
FILE: lib/game/client/fps-counter.js
================================================
/**
*
* Reldens - FPSCounter
*
* Client-side FPS (frames per second) counter utility for displaying game performance metrics.
* Creates a DOM element showing real-time FPS, updated every 60 frames. Displays in a fixed
* position overlay with configurable styling.
*
*/
/**
* @typedef {import('./game-dom').GameDom} GameDom
*/
class FPSCounter
{
/**
* @param {GameDom} gameDom
*/
constructor(gameDom)
{
/** @type {number} */
this.lastFrameTime = performance.now();
/** @type {number} */
this.frameCount = 0;
/** @type {HTMLElement} */
this.fpsDisplay = gameDom.createElementWithStyles('div', 'fps-counter', {
padding: '0 20px',
background: '#000',
color: '#00ff00'
});
gameDom.getElement('.header').appendChild(this.fpsDisplay);
}
updateFPS()
{
let currentTime = performance.now();
let deltaTime = currentTime - this.lastFrameTime;
this.lastFrameTime = currentTime;
const fps = Math.round(1000 / deltaTime);
this.frameCount++;
if(0 === this.frameCount % 60){
this.fpsDisplay.textContent = 'FPS: ' + fps;
}
requestAnimationFrame(this.updateFPS.bind(this));
}
start()
{
this.updateFPS();
}
}
module.exports.FPSCounter = FPSCounter;
================================================
FILE: lib/game/client/game-client.js
================================================
/**
*
* Reldens - GameClient
*
* Manages Colyseus WebSocket client connections for multiplayer rooms. Handles multiserver
* support, automatic connection to global game rooms and feature rooms, client instance creation
* per room, and retry logic for room creation. Coordinates with ConfigManager for server URLs
* and room assignments. Tracks joined rooms across multiple servers.
*
*/
const { Client } = require('colyseus.js');
const { RoomsConst } = require('../../rooms/constants');
const { GameConst } = require('../constants');
const { Logger } = require('@reldens/utils');
/**
* @typedef {import('colyseus.js').Room} Room
* @typedef {import('colyseus.js').Client} Client
* @typedef {import('../../config/client/manager').ConfigManager} ConfigManager
* @typedef {object} JoinOptions
* @property {string} selectedPlayer
* @property {string} token
*/
class GameClient
{
/**
* @param {string} serverUrl
* @param {ConfigManager} config
*/
constructor(serverUrl, config)
{
/** @type {string} */
this.serverUrl = serverUrl;
/** @type {ConfigManager} */
this.config = config;
/** @type {boolean} */
this.autoConnectServerGameRoom = this.config.getWithoutLogs(
'client/rooms/autoConnectServerGameRoom',
true
);
/** @type {boolean} */
this.autoConnectServerFeatureRooms = this.config.getWithoutLogs(
'client/rooms/autoConnectServerFeatureRooms',
true
);
/** @type {Object} */
this.roomsUrls = {};
/** @type {Object} */
this.roomClients = {};
/** @type {Object} */
this.gameRoomsByServer = {};
/** @type {Object} */
this.featuresByServerFlag = {};
/** @type {Object>} */
this.featuresRoomsByServer = {};
/** @type {string} */
this.lastErrorMessage = '';
}
/**
* @param {string} roomName
* @param {JoinOptions} options
* @returns {Promise}
*/
async joinOrCreate(roomName, options)
{
this.lastErrorMessage = '';
try {
let client = this.roomClient(roomName);
if(!client){
Logger.error('Client not found for room name "'+roomName+'".');
return false;
}
let roomUrl = this.roomsUrls[roomName];
await this.connectToGlobalGameRoom(roomUrl, client, options);
await this.connectToGlobalFeaturesRooms(roomUrl, client, options);
return await client.joinOrCreate(roomName, options);
} catch (error) {
if(RoomsConst.ERRORS.CREATING_ROOM_AWAIT === error.message){
await new Promise(resolve => setTimeout(resolve, 500));
return await this.joinOrCreate(roomName, options);
}
this.lastErrorMessage = error.message;
// any connection errors will be handled in the higher level class
Logger.error('Joining room error: '+error.message);
}
return false;
}
/**
* @param {string} roomUrl
* @param {Client} client
* @param {JoinOptions} options
* @returns {Promise}
*/
async connectToGlobalGameRoom(roomUrl, client, options)
{
if(!this.autoConnectServerGameRoom){
return;
}
if('' === roomUrl || this.serverUrl === roomUrl){
Logger.debug('Avoid connect to global game room.', this.serverUrl, roomUrl);
return;
}
if(this.gameRoomsByServer[roomUrl]){
return;
}
Logger.debug('Registering GameRoom for server: '+roomUrl);
this.gameRoomsByServer[roomUrl] = await client.joinOrCreate(GameConst.ROOM_GAME, options);
// required to avoid unregistered messages warning:
this.gameRoomsByServer[roomUrl].onMessage('*', () => {});
}
/**
* @param {string} roomUrl
* @param {Client} client
* @param {JoinOptions} options
* @returns {Promise}
*/
async connectToGlobalFeaturesRooms(roomUrl, client, options)
{
if(!this.autoConnectServerFeatureRooms){
return;
}
if('' === roomUrl || this.serverUrl === roomUrl){
Logger.debug('Avoid connect to features rooms.', this.serverUrl, roomUrl);
return;
}
if(this.featuresByServerFlag[roomUrl]){
return;
}
Logger.debug('Registering features rooms for server: '+roomUrl);
this.featuresByServerFlag[roomUrl] = true;
let featuresRoomsNames = this.config.getWithoutLogs('client/rooms/featuresRoomsNames', []);
if(0 < featuresRoomsNames.length){
return;
}
this.featuresRoomsByServer[roomUrl] = {};
for(let featureRoomName of featuresRoomsNames){
this.featuresRoomsByServer[roomUrl][featureRoomName] = await client.joinOrCreate(featureRoomName, options);
}
}
/**
* @param {string} roomName
* @returns {Client}
*/
roomClient(roomName)
{
if(!this.roomClients[roomName]){
this.roomsUrls[roomName] = this.config.getWithoutLogs('client/rooms/servers/'+roomName, this.serverUrl);
Logger.debug('Creating client with URL "'+this.roomsUrls[roomName]+'" for room "'+roomName+'".');
this.roomClients[roomName] = new Client(
this.roomsUrls[roomName]
);
}
return this.roomClients[roomName];
}
}
module.exports.GameClient = GameClient;
================================================
FILE: lib/game/client/game-dom.js
================================================
/**
*
* Reldens - GameDom
*
* Singleton utility class for DOM manipulation and element management. Provides helpers for
* querying elements, creating elements with styles, updating content, appending HTML, removing
* elements, and handling AJAX requests. Manages CSS style suffixes (px for positioning/sizing).
* Exported as a singleton instance for shared use across the client application.
*
*/
const { sc } = require('@reldens/utils');
class GameDom
{
constructor()
{
/** @type {Object} */
this.styleSuffix = {
width: 'px',
height: 'px',
top: 'px',
bottom: 'px',
left: 'px',
right: 'px'
};
}
/**
* @returns {Window}
*/
getWindow()
{
return window;
}
/**
* @returns {Document}
*/
getDocument()
{
return window.document;
}
/**
* @param {string} querySelector
* @param {HTMLElement|Document|boolean} [container]
* @returns {HTMLElement|null}
*/
getElement(querySelector, container = false)
{
if(!querySelector){
return null;
}
return (container || document).querySelector(querySelector);
}
/**
* @param {string} querySelector
* @param {HTMLElement|Document} [container]
* @returns {NodeListOf