diff --git a/.env.dist b/.env.dist index abb36d294..bcea0e9c5 100644 --- a/.env.dist +++ b/.env.dist @@ -36,8 +36,8 @@ RELDENS_DB_LIMIT=0 # RELDENS_STORAGE_DRIVER=mikro-orm RELDENS_STORAGE_DRIVER=objection-js # Logs: -RELDENS_LOG_LEVEL=1 -# RELDENS_ENABLE_TRACE_FOR= +RELDENS_LOG_LEVEL=9 +RELDENS_ENABLE_TRACE_FOR="emergency,alert,critical,error,warning" # Mailer: RELDENS_MAILER_ENABLE=0 RELDENS_MAILER_SERVICE=gmail @@ -53,6 +53,9 @@ RELDENS_MAILER_FROM=youremail@gmail.com # RELDENS_ON_BUNDLE_COPY_ASSETS=1 # Use Express to serve static files already bundled. RELDENS_EXPRESS_SERVE_STATICS=1 +# Game Server: +RELDENS_PING_INTERVAL=5000 +RELDENS_PING_MAX_RETRIES=3 # Admin: RELDENS_ADMIN_SECURE_LOGIN=1 RELDENS_ADMIN_ROUTE_PATH="/reldens-admin" diff --git a/.gitignore b/.gitignore index d0c1f5b53..7ec9e0d58 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,6 @@ /.env /.idea /knexfile.js -/migrations/* -!/migrations/production /node_modules /npm-debug* /test diff --git a/lib/actions/client/preloader-handler.js b/lib/actions/client/preloader-handler.js index 6352be3c1..b9c333181 100644 --- a/lib/actions/client/preloader-handler.js +++ b/lib/actions/client/preloader-handler.js @@ -5,6 +5,7 @@ */ const { Logger, sc } = require('@reldens/utils'); +const { GameConst } = require('../../game/constants'); class PreloaderHandler { @@ -100,8 +101,8 @@ class PreloaderHandler if(sc.hasOwn(data.animationData, ['type', 'img']) && 'spritesheet' === data.animationData.type){ this.preloadAnimationsInDirections(data, uiScene); } - if(data.classKey && sc.isFunction(data.classKey, 'prepareAnimation')){ - data.classKey.prepareAnimation({data, uiScene, pack: this}); + if(data.classKey && sc.isFunction(data.classKey['prepareAnimation'])){ + data.classKey['prepareAnimation']({data, uiScene, pack: this}); } } @@ -144,24 +145,26 @@ class PreloaderHandler { if(sc.hasOwn(data.animationData, ['type', 'img']) && data.animationData.type === 'spritesheet'){ let animDir = sc.get(data.animationData, 'dir', 0); - if(0 < 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(animDir === 1 || animDir === 2){ - this.createWithDirection(data, uiScene, 'up'); - this.createWithDirection(data, uiScene, 'down'); - } - if(animDir === 1 || animDir === 3){ - this.createWithDirection(data, uiScene, 'left'); - this.createWithDirection(data, uiScene, 'right'); - } - } else { - this.createWithDirection(data, uiScene); - } + 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}); } - if(data.classKey && sc.isFunction(data.classKey, 'createAnimation')){ - data.classKey.createAnimation({data, uiScene, pack: this}); + } + + 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); } } diff --git a/lib/actions/client/skills-ui.js b/lib/actions/client/skills-ui.js index d60eed62a..fb9e3fd2a 100644 --- a/lib/actions/client/skills-ui.js +++ b/lib/actions/client/skills-ui.js @@ -49,6 +49,7 @@ class SkillsUi 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); diff --git a/lib/actions/server/player-enricher.js b/lib/actions/server/player-enricher.js index 33fd27dcc..6317853e6 100644 --- a/lib/actions/server/player-enricher.js +++ b/lib/actions/server/player-enricher.js @@ -6,9 +6,9 @@ const { Pvp } = require('./pvp'); const { SkillsExtraData } = require('./skills/skills-extra-data'); +const { ClientWrapper } = require('../../game/server/client-wrapper'); const { Logger, sc } = require('@reldens/utils'); const { SkillConst } = require('@reldens/skills'); -const { ClientWrapper } = require('../../game/server/client-wrapper'); const SkillsServer = require('@reldens/skills/lib/server'); class PlayerEnricher @@ -29,6 +29,7 @@ class PlayerEnricher if(!classPath){ continue; } + // @TODO - BETA - Make label configurable. player.additionalLabel = ' - LvL '+classPath.currentLevel+' - '+classPath.owner_full_class_path.label; player.currentClassPathLabel = player.avatarKey = classPath.owner_full_class_path.key; } @@ -51,7 +52,7 @@ class PlayerEnricher { return async (target, executedSkill) => { let messageData = Object.assign({skillKey: executedSkill.key}, executedSkill.owner.getPosition()); - if(sc.isFunction(executedSkill.owner, 'getSkillExtraData')){ + if(sc.isObjectFunction(executedSkill.owner, 'getSkillExtraData')){ let params = {skill: executedSkill, target}; Object.assign(messageData, {extraData: executedSkill.owner.getSkillExtraData(params)}); } @@ -63,27 +64,27 @@ class PlayerEnricher ); let from = {x: currentPlayer.state.x, y: currentPlayer.state.y}; executedSkill.initialPosition = from; - let to = {x: target.state.x, y: target.state.y}; let animData = sc.get(room.config.client.skills.animations, 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: - if(currentPlayer.physicalBody){ - if(!currentPlayer.physicalBody.world){ - Logger.error('PhysicalBody world is null.', currentPlayer.physicalBody.id); - return false; - } - currentPlayer.physicalBody.world.shootBullet(from, to, executedSkill); + 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; } - return false; + physicalBody.world.shootBullet(from, {x: target.state.x, y: target.state.y}, executedSkill); }; } static async withSkillsServerAndClassPath(props) { let {client, currentPlayer, room, skillsModelsManager, dataServer, events} = props; - // prepare player classPath and skills data: let classPathData = await skillsModelsManager.prepareClassPathData( currentPlayer, 'player_id', @@ -100,7 +101,6 @@ class PlayerEnricher affectedProperty: room.config.get('client/actions/skills/affectedProperty'), client: new ClientWrapper({client, room}) }); - // append skills server to player: currentPlayer.skillsServer = new SkillsServer(classPathData); currentPlayer.avatarKey = classPathData.key; } diff --git a/lib/actions/server/pve.js b/lib/actions/server/pve.js index 7645d535e..64731964e 100644 --- a/lib/actions/server/pve.js +++ b/lib/actions/server/pve.js @@ -30,7 +30,7 @@ class Pve extends Battle { // @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 how the current enemy-object was implemented, for example the PVE can start when the player just + // 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}); @@ -39,17 +39,23 @@ class Pve extends Battle // attack for which matter we won't start the battle until the physical body hits the target. return false; } - 0 < target.stats[roomScene.config.get('client/actions/skills/affectedProperty')] - ? await this.startBattleWith(playerSchema, roomScene) - // physical attacks or effects will run the battleEnded, normal attacks or effects will hit this case: - : await this.battleEnded(playerSchema, roomScene); + if(0 < target.stats[roomScene.config.get('client/actions/skills/affectedProperty')]){ + 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); } async startBattleWith(playerSchema, room) { // @TODO - BETA - Yeah... a lot could happen and this could be improved by cleaning the timers on specific // actions like when player disconnects. - if(!room || !room.roomWorld || !playerSchema || !room.state || !room.playerByIdFromState(playerSchema.sessionId)){ + if( + !room?.roomWorld + || !room?.state + || !playerSchema + || !room.playerBySessionIdFromState(playerSchema.sessionId) + ){ // @NOTE: leaveBattle is used for when the player can't be reached anymore or disconnected. this.leaveBattle(playerSchema); return false; @@ -165,6 +171,7 @@ class Pve extends Battle await this.targetObject.respawn(room); this.sendBattleEndedActionData(room, playerSchema, actionData); await this.events.emit(this.targetObject.getBattleEndEvent(), playerSchema, this, actionData); + await this.events.emit('reldens.battleEnded', {playerSchema, pve: this, actionData, room}); } sendBattleEndedActionData(room, playerSchema, actionData) diff --git a/lib/actions/server/skills/type-physical-attack.js b/lib/actions/server/skills/type-physical-attack.js index 99363e52d..b9ec3bfc1 100644 --- a/lib/actions/server/skills/type-physical-attack.js +++ b/lib/actions/server/skills/type-physical-attack.js @@ -120,7 +120,7 @@ class TypePhysicalAttack extends PhysicalAttack // 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.playerByIdFromState(playerId); + return this.room.playerBySessionIdFromState(playerId); } return props[defenderBodyKey].roomObject; } diff --git a/lib/actions/server/skills/type-physical-effect.js b/lib/actions/server/skills/type-physical-effect.js index b603b48fa..9f5394d86 100644 --- a/lib/actions/server/skills/type-physical-effect.js +++ b/lib/actions/server/skills/type-physical-effect.js @@ -108,7 +108,7 @@ class TypePhysicalEffect extends PhysicalEffect { // we already validate if one of the bodies is a bullet so the other will be always a player or an object: return sc.hasOwn(props[defenderBodyKey], 'playerId') ? - this.room.playerByIdFromState(props[defenderBodyKey].playerId) : props[defenderBodyKey].roomObject; + this.room.playerBySessionIdFromState(props[defenderBodyKey].playerId) : props[defenderBodyKey].roomObject; } removeBullet(body) diff --git a/lib/audio/client/manager.js b/lib/audio/client/manager.js index 5fcb4dcc8..2641057b6 100644 --- a/lib/audio/client/manager.js +++ b/lib/audio/client/manager.js @@ -72,7 +72,10 @@ class AudioManager { 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. Audio key:', audio.audio_key); + Logger.error( + 'Audio file does not exists. Audio key: ' + audio.audio_key, + onScene.cache.audio.entries.entries + ); return false; } let audioInstance = onScene.sound.add(audio.audio_key, soundConfig); @@ -170,7 +173,10 @@ class AudioManager continue; } let audioLoader = currentScene.load.audio(audio.audio_key, filesArr); - audioLoader.on('complete', async () => { + 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.', { diff --git a/lib/chat/cleaner.js b/lib/chat/cleaner.js index 36ed85510..3812cf0fd 100644 --- a/lib/chat/cleaner.js +++ b/lib/chat/cleaner.js @@ -2,21 +2,17 @@ * * Reldens - Cleaner * - * Clean chat messages. - * */ +const { sc } = require('@reldens/utils'); + class Cleaner { cleanMessage(message, characterLimit) { // @TODO - BETA - Implement any clean feature here. - let text = message.toString().replace(/\\/g, ''); - if(0 < characterLimit){ - return text.substring(0, characterLimit); - } - return text; + return sc.cleanMessage(message, characterLimit); } } diff --git a/lib/chat/client/chat-ui.js b/lib/chat/client/chat-ui.js index ed1bd8933..d5d41db26 100644 --- a/lib/chat/client/chat-ui.js +++ b/lib/chat/client/chat-ui.js @@ -26,6 +26,7 @@ class ChatUi createUi() { + // @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; @@ -179,6 +180,8 @@ class ChatUi box.classList.remove('hidden'); this.uiChat.setDepth(4); this.chatOpenButton?.classList.add('hidden'); + let readPanel = this.uiChat.getChildByProperty('id', ChatConst.CHAT_MESSAGES); + readPanel.scrollTo(0, readPanel.scrollHeight); this.hideNotificationsBalloon(); } @@ -245,9 +248,9 @@ class ChatUi } if(!this.uiChat.getChildByProperty('id', ChatConst.CHAT_UI).classList.contains('hidden')){ readPanel.scrollTo(0, readPanel.scrollHeight); - } else { - this.showNotificationBalloon(); + return; } + this.showNotificationBalloon(); } isValidMessageType(messageType) diff --git a/lib/chat/constants.js b/lib/chat/constants.js index 764524d17..70d750728 100644 --- a/lib/chat/constants.js +++ b/lib/chat/constants.js @@ -26,7 +26,9 @@ module.exports.ChatConst = { CHAT_SYSTEM: 's', CHAT_PRIVATE: 'p', CHAT_DAMAGE: 'd', + CHAT_REWARD: 'r', CHAT_SKILL: 'ss', + CHAT_TEAMS: 'ct', // @TODO - BETA - Refactor to CHAT_TYPE: {} CHAT_TYPE_NORMAL: 'ctn', CHAT_TYPE_PRIVATE_FROM: 'ctpf', @@ -36,6 +38,8 @@ module.exports.ChatConst = { CHAT_TYPE_SYSTEM_ERROR: 'ctse', CHAT_TYPE_SYSTEM_BATTLE: 'ctse', CHAT_TYPE_SYSTEM_BATTLE_MODIFIERS: 'ctsem', + CHAT_TYPE_REWARD: 'ctr', + CHAT_TYPE_TEAMS: 'ctt', // @TODO - BETA - Refactor to COLORS. colors: { ctn: '#ffffff', @@ -50,6 +54,7 @@ module.exports.ChatConst = { MESSAGES: { ON: ' on ', HIT_ON: ' hit on ', - DODGED: ' dodged ' + DODGED: ' dodged ', + REWARD: 'You obtained %dropQuantity %itemLabel' } }; diff --git a/lib/chat/server/event-listener/npc-skills.js b/lib/chat/server/event-listener/npc-skills.js index 1e668e20e..15244ae60 100644 --- a/lib/chat/server/event-listener/npc-skills.js +++ b/lib/chat/server/event-listener/npc-skills.js @@ -21,7 +21,6 @@ class NpcSkills this.listenDamageEvent(attackSkill, chatConfig, chatManager); this.listenModifiersEvent(effectSkill, chatConfig, chatManager); this.listenAfterRunLogicEvent((attackSkill || effectSkill), chatConfig, chatManager); - } static listenDamageEvent(attackSkill, chatConfig, chatManager) diff --git a/lib/chat/server/message-actions.js b/lib/chat/server/message-actions.js index e182347b2..96c0b352a 100644 --- a/lib/chat/server/message-actions.js +++ b/lib/chat/server/message-actions.js @@ -8,9 +8,9 @@ const { ChatManager } = require('./manager'); const { Cleaner } = require('../cleaner'); -const { Logger, sc } = require('@reldens/utils'); const { ChatConst } = require('../constants'); const { GameConst } = require('../../game/constants'); +const { Logger, sc } = require('@reldens/utils'); class ChatMessageActions { diff --git a/lib/chat/server/plugin.js b/lib/chat/server/plugin.js index 46da566cf..12649990e 100644 --- a/lib/chat/server/plugin.js +++ b/lib/chat/server/plugin.js @@ -18,11 +18,11 @@ class ChatPlugin extends PluginInterface setup(props) { this.events = sc.get(props, 'events', false); - if(!this.events){ + if (!this.events) { Logger.error('EventsManager undefined in ChatPlugin.'); } this.dataServer = sc.get(props, 'dataServer', false); - if(!this.dataServer){ + if (!this.dataServer) { Logger.error('DataServer undefined in ChatPlugin.'); } this.chatManager = new ChatManager({dataServer: this.dataServer}); @@ -31,7 +31,7 @@ class ChatPlugin extends PluginInterface // then we can use the event manager to append the feature in every action required: 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}); + roomsList.push({ roomName: 'chat', room: RoomChat }); }); this.events.on('reldens.serverConfigFeaturesReady', (props) => { this.chatConfig = props.configProcessor.get('client/ui/chat'); diff --git a/lib/chat/server/room-chat.js b/lib/chat/server/room-chat.js index 244fc611a..2bab13ce8 100644 --- a/lib/chat/server/room-chat.js +++ b/lib/chat/server/room-chat.js @@ -2,15 +2,13 @@ * * Reldens - RoomChat * - * This object room is used for global chat and private chat between users. - * */ const { RoomLogin } = require('../../rooms/server/login'); const { ChatManager } = require('./manager'); const { Cleaner } = require('../cleaner'); -const { Logger, ErrorManager, sc} = require('@reldens/utils'); const { ChatConst } = require('../constants'); +const { Logger, ErrorManager, sc } = require('@reldens/utils'); class RoomChat extends RoomLogin { diff --git a/lib/config/constants.js b/lib/config/constants.js index 2a4777ae4..1ddd68ec6 100644 --- a/lib/config/constants.js +++ b/lib/config/constants.js @@ -8,8 +8,9 @@ // constants: module.exports.ConfigConst = { - CONFIG_TYPE_BOOLEAN: 'b', - CONFIG_TYPE_NUMBER: 'i', - CONFIG_TYPE_TEXT: 't', - CONFIG_TYPE_JSON: 'j' + CONFIG_TYPE_TEXT: 1, + CONFIG_TYPE_FLOAT: 2, + CONFIG_TYPE_BOOLEAN: 3, + CONFIG_TYPE_JSON: 4, + CONFIG_TYPE_COMMA_SEPARATED: 5 }; diff --git a/lib/config/server/manager.js b/lib/config/server/manager.js index 22e2d5e82..f8daa8d30 100644 --- a/lib/config/server/manager.js +++ b/lib/config/server/manager.js @@ -81,12 +81,12 @@ class ConfigManager extends ConfigProcessor { await this.events.emit('reldens.beforeGetParsedValue', {configManager: this, config: config}); if(config.type === ConfigConst.CONFIG_TYPE_TEXT){ - return config.value; + return config.value.toString(); } if(config.type === ConfigConst.CONFIG_TYPE_BOOLEAN){ return !(config.value === 'false' || config.value === '0'); } - if(config.type === ConfigConst.CONFIG_TYPE_NUMBER){ + if(config.type === ConfigConst.CONFIG_TYPE_FLOAT){ return parseFloat(config.value); } if(config.type === ConfigConst.CONFIG_TYPE_JSON){ @@ -96,6 +96,9 @@ class ConfigManager extends ConfigProcessor Logger.error('Invalid JSON on configuration:', config); } } + if(config.type === ConfigConst.CONFIG_TYPE_COMMA_SEPARATED){ + return config.value.split(','); + } return config.value; } diff --git a/lib/features/client/config-client.js b/lib/features/client/config-client.js index 8839da86c..64427c72e 100644 --- a/lib/features/client/config-client.js +++ b/lib/features/client/config-client.js @@ -16,6 +16,8 @@ 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 { RewardsPlugin } = require('../../rewards/client/plugin'); module.exports.ClientCoreFeatures = { chat: ChatPlugin, @@ -25,5 +27,7 @@ module.exports.ClientCoreFeatures = { users: UsersPlugin, audio: AudioPlugin, rooms: RoomsPlugin, - prediction: PredictionPlugin + prediction: PredictionPlugin, + teams: TeamsPlugin, + rewards: RewardsPlugin }; diff --git a/lib/features/server/config-server.js b/lib/features/server/config-server.js index 01dbbf34a..410d8955f 100644 --- a/lib/features/server/config-server.js +++ b/lib/features/server/config-server.js @@ -17,6 +17,8 @@ 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'); module.exports.ServerCoreFeatures = { chat: ChatPlugin, @@ -27,5 +29,7 @@ module.exports.ServerCoreFeatures = { users: UsersPlugin, audio: AudioPlugin, rooms: RoomsPlugin, - admin: AdminPlugin + admin: AdminPlugin, + teams: TeamsPlugin, + rewards: RewardsPlugin }; diff --git a/lib/features/server/manager.js b/lib/features/server/manager.js index e1a05aafa..c2068763a 100644 --- a/lib/features/server/manager.js +++ b/lib/features/server/manager.js @@ -16,7 +16,6 @@ class FeaturesManager { // all available features listed in config file: this.availableFeatures = ServerCoreFeatures; - // initialize features props: this.featuresList = {}; this.featuresCodeList = []; this.events = sc.get(props, 'events', false); @@ -24,14 +23,16 @@ class FeaturesManager Logger.error('EventsManager undefined in FeaturesManager.'); } this.dataServer = sc.get(props, 'dataServer', false); + if(!this.dataServer){ + Logger.error('dataServer undefined in FeaturesManager.'); + } + this.config = sc.get(props, 'config', {}); } async loadFeatures() { - // get the features from the database: let featuresCollection = await this.dataServer.getEntity('features').loadBy('is_enabled', 1); for(let featureEntity of featuresCollection){ - // add the feature to the codes list: 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. @@ -46,13 +47,19 @@ class FeaturesManager // set package on entity: featureEntity.package = new featurePackage(); if('function' === typeof featureEntity.package.setup){ - await featureEntity.package.setup({events: this.events, dataServer: this.dataServer}); + await featureEntity.package.setup({ + events: this.events, + dataServer: this.dataServer, + featuresManager: this, + config: this.config + }); } // 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; } diff --git a/lib/game/client/game-dom.js b/lib/game/client/game-dom.js index e05d0bd77..5bcaa5711 100644 --- a/lib/game/client/game-dom.js +++ b/lib/game/client/game-dom.js @@ -68,7 +68,7 @@ class GameDom insideInput() { - return (this.activeElement().tagName.toLowerCase() === 'input'); + return 'input' === this.activeElement().tagName.toLowerCase(); } getJSON(url, callback) diff --git a/lib/game/client/game-engine.js b/lib/game/client/game-engine.js index ec8234b4c..2d40e40f7 100644 --- a/lib/game/client/game-engine.js +++ b/lib/game/client/game-engine.js @@ -2,12 +2,10 @@ * * Reldens - GameEngine * - * This class will extend Phaser to include any all the customizations. - * */ -const { Game, Input } = require('phaser'); const TemplateEngine = require('mustache'); +const { Game, Input } = require('phaser'); const { GameConst } = require('../constants'); const { ObjectsConst } = require('../../objects/constants'); const { sc } = require('@reldens/utils'); @@ -42,20 +40,19 @@ class GameEngine extends Game updateGameSize(manager) { - // get the window size: let {newWidth, newHeight} = this.getCurrentScreenSize(manager); - // @TODO - BETA - Make timeout 500 configurable. setTimeout(() => { this.eventsManager.emit('reldens.updateGameSizeBefore', this, newWidth, newHeight); this.scale.setGameSize(newWidth, newHeight); for(let key of Object.keys(this.uiScene.elementsUi)){ - let {uiX, uiY} = this.uiScene.getUiConfig(key, newWidth, newHeight); let uiElement = this.uiScene.elementsUi[key]; + let positionKey = sc.get(this.uiScene.userInterfaces[key], 'uiPositionKey', key); + let {uiX, uiY} = this.uiScene.getUiConfig(positionKey, newWidth, newHeight); uiElement.x = uiX; uiElement.y = uiY; } this.eventsManager.emit('reldens.updateGameSizeAfter', this, newWidth, newHeight); - }, 500); + }, manager.config.getWithoutLogs('client/general/gameEngine/updateGameSizeTimeOut', 500)); } getCurrentScreenSize(manager) @@ -67,7 +64,6 @@ class GameEngine extends Game let mapWidth = 0, mapHeight = 0; let activeScene = manager.getActiveScene(); if(activeScene && activeScene.map){ - // get the map max values and use the mapWidth = activeScene.map.width * activeScene.map.tileWidth; newWidth = Math.min(containerWidth, mapWidth); mapHeight = activeScene.map.height * activeScene.map.tileHeight; @@ -91,26 +87,44 @@ class GameEngine extends Game targetDisplay(targetName, target) { - // @TODO - BETA - Refactor. - let gameManager = this.uiScene.gameManager; - let showPlayedTime = gameManager.config.get('client/players/playedTime/show'); - if(0 === showPlayedTime || GameConst.TYPE_PLAYER !== target.type){ - return targetName; + let targetDisplayContent = targetName; + if(GameConst.TYPE_PLAYER === target.type){ + targetDisplayContent += this.generateTargetPlayedTime(target); } - let currentPlayer = gameManager.getCurrentPlayer(); - let timeText = ''; - let label = gameManager.config.get('client/players/playedTime/label'); - if(0 < showPlayedTime && currentPlayer.playerId === target.id){ - let element = gameManager.gameDom.createElement('p'); - element.innerHTML = label+(currentPlayer.playedTime / 60 / 60).toFixed(1)+'hs'; - timeText = element.outerHTML; + return targetDisplayContent; + } + + generateTargetPlayedTime(target) + { + let playerTimeText = ''; + let showPlayedTimeConfig = this.uiScene.gameManager.config.getWithoutLogs( + 'client/players/playedTime/show', + GameConst.SHOW_PLAYER_TIME.ONLY_OWN_PLAYER + ); + if(GameConst.SHOW_PLAYER_TIME.NONE === showPlayedTimeConfig){ + return playerTimeText; } - if(2 === showPlayedTime && sc.hasOwn(currentPlayer.players, target.id)){ - let element = gameManager.gameDom.createElement('p'); - element.innerHTML = label+(currentPlayer.players[target.id].playedTime / 60 / 60).toFixed(1)+'hs'; - timeText = element.outerHTML; + let currentPlayer = this.uiScene.gameManager.getCurrentPlayer(); + if(GameConst.SHOW_PLAYER_TIME.ALL_PLAYERS === showPlayedTimeConfig || currentPlayer.playerId === target.id){ + let targetPlayedTime = this.obtainPlayedTime(target, currentPlayer); + playerTimeText += this.createPlayedTimeLabel(targetPlayedTime); } - return targetName+timeText; + return playerTimeText; + } + + createPlayedTimeLabel(playedTime) + { + let htmlElement = this.uiScene.gameManager.gameDom.createElement('p'); + htmlElement.innerHTML = this.uiScene.gameManager.config.get('client/players/playedTime/label').replace( + '%playedTimeInHs', + playedTime + ); + return htmlElement.outerHTML; + } + + obtainPlayedTime(target, currentPlayer) + { + return (currentPlayer.players[target.id].playedTime / 60 / 60).toFixed(1); } clearTarget() diff --git a/lib/game/client/instructions-ui.js b/lib/game/client/instructions-ui.js index d46cace19..767ffb712 100644 --- a/lib/game/client/instructions-ui.js +++ b/lib/game/client/instructions-ui.js @@ -9,6 +9,7 @@ class InstructionsUi createInstructions(instConfig, scenePreloader) { + // @TODO - BETA - Replace by UserInterface. scenePreloader.elementsUi['instructions'] = scenePreloader.add.dom(instConfig.uiX, instConfig.uiY) .createFromCache('instructions'); let instructionsBox = scenePreloader.gameManager.gameDom.getElement('#instructions'); diff --git a/lib/game/client/manager.js b/lib/game/client/manager.js index 94dbd01a5..fb14deb9d 100644 --- a/lib/game/client/manager.js +++ b/lib/game/client/manager.js @@ -2,8 +2,6 @@ * * Reldens - GameManager * - * Like the ServerManager class, this is the main one that will handle everything on the client side. - * */ const { GameClient } = require('./game-client'); @@ -14,7 +12,7 @@ const { GameDom } = require('./game-dom'); const { ConfigManager } = require('../../config/client/config-manager'); const { GameConst } = require('../constants'); const { FirebaseConnector } = require('../../firebase/client/connector'); -const { EventsManagerSingleton, Logger, sc } = require('@reldens/utils'); +const { ErrorManager, EventsManagerSingleton, Logger, sc } = require('@reldens/utils'); class GameManager { @@ -132,7 +130,7 @@ class GameManager { await this.events.emit('reldens.beforeInitEngineAndStartGame', this.initialGameData, this); if(!sc.hasOwn(this.initialGameData, 'gameConfig')){ - throw new Error('ERROR - Missing game configuration.'); + ErrorManager.error('Missing game configuration.'); } // apply the initial config to the processor: sc.deepMergeProperties(this.config, (this.initialGameData?.gameConfig || {})); @@ -289,6 +287,15 @@ class GameManager return activeScene.player; } + currentPlayerName() + { + let currentPlayer = this.getCurrentPlayer(); + if(!currentPlayer){ + return ''; // @TODO - BETA - Make constant configurable. + } + return currentPlayer.player_id+' - '+currentPlayer.playerName; + } + getCurrentPlayerAnimation() { let current = this.getCurrentPlayer(); @@ -324,6 +331,7 @@ class GameManager displayForgotPassword() { + // @TODO - BETA - Extract. this.gameDom.getJSON(this.appServerUrl+'/reldens-mailer-enabled', (err, response) => { if(!response.enabled){ return; diff --git a/lib/game/client/minimap-ui.js b/lib/game/client/minimap-ui.js index 49eb6b5ae..8c787c819 100644 --- a/lib/game/client/minimap-ui.js +++ b/lib/game/client/minimap-ui.js @@ -9,6 +9,7 @@ class MinimapUi createMinimap(minimapConfig, scenePreloader) { + // @TODO - BETA - Replace by UserInterface. scenePreloader.elementsUi['minimap'] = scenePreloader.add.dom(minimapConfig.uiX, minimapConfig.uiY) .createFromCache('minimap'); let openButton = scenePreloader.elementsUi['minimap'].getChildByProperty('id', 'minimap-open'); diff --git a/lib/game/client/minimap.js b/lib/game/client/minimap.js index ab5b569f0..1b35faa40 100644 --- a/lib/game/client/minimap.js +++ b/lib/game/client/minimap.js @@ -18,6 +18,7 @@ class Minimap createMap(scene, playerSprite) { + // @TODO - BETA - Improve camera. this.autoWidth = scene.map.widthInPixels / sc.get(this.config, 'mapWidthDivisor', 1); this.camWidth = sc.get(this.config, 'fixedWidth', this.autoWidth); this.autoHeight = scene.map.heightInPixels / sc.get(this.config, 'mapHeightDivisor', 1); @@ -30,9 +31,18 @@ class Minimap .setName('minimap') .setBackgroundColor(this.camBackgroundColor) .setZoom(this.camZoom) - .startFollow(playerSprite) + .startFollow( + playerSprite, + sc.get(this.config, 'mapCameraRoundPixels', true), + sc.get(this.config, 'mapCameraLerpX', 1), + sc.get(this.config, 'mapCameraLerpY', 1) + ) .setRoundPixels(true) - .setVisible(false); + .setVisible(false) + .setOrigin( + sc.get(this.config, 'mapCameraOriginX', 0.18), + sc.get(this.config, 'mapCameraOriginY', 0.18) + ); this.roundMap = sc.get(this.config, 'roundMap', false); if(this.roundMap){ // @NOTE: because of the camara zoom the circle size append to the preload scene is different from the map diff --git a/lib/game/client/room-events.js b/lib/game/client/room-events.js index 97b91a80c..9104ef825 100644 --- a/lib/game/client/room-events.js +++ b/lib/game/client/room-events.js @@ -2,9 +2,6 @@ * * Reldens - RoomEvents * - * This class will listen the scene-rooms and run the related actions, it will also register the other modules action - * into the room events. - * */ const { PlayerEngine } = require('../../users/client/player-engine'); @@ -26,6 +23,7 @@ class RoomEvents this.gameEngine = gameManager.gameEngine; this.roomName = roomName; this.events = gameManager.events; + // @TODO - BETA - Move the following inside a single property called "metadata". this.objectsUi = {}; this.tradeUi = {}; } @@ -97,7 +95,8 @@ class RoomEvents dir: player.state.dir, playerName: player.playerName, avatarKey: player.avatarKey, - playedTime: player.playedTime + playedTime: player.playedTime, + player_id: player.player_id }; if(this.isCurrentPlayer(key)){ return await this.createCurrentPlayer(player, previousScene, key); @@ -164,15 +163,20 @@ class RoomEvents playersOnRemove(player, key) { this.events.emitSync('reldens.playersOnRemove', player, key, this); - key === this.room.sessionId ? this.gameOverReload() : this.removePlayerByKey(key); + if(key === this.room.sessionId){ + return this.gameOverReload(); + } + return this.removePlayerByKey(key); } removePlayerByKey(key) { let currentScene = this.getActiveScene(); if(this.playerExists(currentScene, key)){ - // remove your player entity from the game world: currentScene.player.removePlayer(key); + if(currentScene.player.currentTarget?.id === key){ + this.gameEngine.clearTarget(); + } } } @@ -216,15 +220,16 @@ class RoomEvents async closeBox(message) { - if(GameConst.CLOSE_UI_ACTION === message.act && '' !== message.id){ - let closeButton = this.gameManager.gameDom.getElement('#box-'+message.id+' .box-close'); - if(closeButton){ - closeButton.click(); - return true; - } + if(GameConst.CLOSE_UI_ACTION !== message.act || !message.id){ + return false; + } + let closeButton = this.gameManager.gameDom.getElement('#box-'+message.id+' .box-close'); + if(!closeButton){ Logger.error('Box could not be closed ID "'+message.id+'".'); return false; } + closeButton.click(); + return true; } async runCustomMessageListener(message) @@ -243,11 +248,11 @@ class RoomEvents Logger.error('Listener "'+listenerKey+'" is missing.'); return false; } - if(!sc.isFunction(listener, 'executeClientMessageActions')){ + if(!sc.isFunction(listener['executeClientMessageActions'])){ Logger.error('Listener is missing "executeClientMessageActions" method.', listener); return false; } - listener.executeClientMessageActions({message, roomEvents: this}); + listener['executeClientMessageActions']({message, roomEvents: this}); } async runUpdateStats(message) @@ -280,24 +285,26 @@ class RoomEvents ){ return false; } + await this.events.emit('reldens.startChangedScene', {message, roomEvents: this}); this.closeAllActiveDialogs(); - await this.events.emit('reldens.changedScene', message, this); let currentScene = this.getActiveScene(); // if other users enter the current scene we need to add them: - let {id, x, y, dir, playerName, playedTime, avatarKey} = message; + let {id, x, y, dir, playerName, playedTime, avatarKey, player_id} = message; let topOff = this.gameManager.config.get('client/players/size/topOffset'); let leftOff = this.gameManager.config.get('client/players/size/leftOffset'); - let addPlayerData = {x: (x - leftOff), y: (y - topOff), dir, playerName, playedTime, avatarKey}; + let addPlayerData = {x: (x - leftOff), y: (y - topOff), dir, playerName, playedTime, avatarKey, player_id}; currentScene.player.addPlayer(id, addPlayerData); + await this.events.emit('reldens.endChangedScene', {message, roomEvents: this}); } closeAllActiveDialogs() { let closeButtons = this.gameManager.gameDom.getElements('.ui-box .box-close'); - if(0 < closeButtons.length){ - for(let closeButton of closeButtons){ - closeButton.click(); - } + if(0 === closeButtons.length){ + return; + } + for(let closeButton of closeButtons){ + closeButton.click(); } } @@ -336,13 +343,13 @@ class RoomEvents this.gameManager.gameDom.getElement('#game-over').classList.remove('hidden'); } - roomOnLeave(code) + async roomOnLeave(code) { // @TODO - BETA - Improve disconnection handler. - // server disconnection handler: if(code > 1001 && !this.gameManager.gameOver && !this.gameManager.forcedDisconnection){ Logger.error('There was a connection error.', ['Error Code:', code]); } + await this.events.emit('reldens.playerLeftScene', {code, roomEvents: this}); // @NOTE: the client can initiate the disconnection, this is also triggered when the users change the room. } @@ -353,6 +360,7 @@ class RoomEvents } let currentScene = this.getActiveScene(); if(!currentScene.player || !sc.hasOwn(currentScene.player.players, this.room.sessionId)){ + // @TODO - BETA - Refactor and fix cases. Logger.error('For some reason you hit this case which should not happen.', this.room, currentScene); return false; } @@ -385,15 +393,16 @@ class RoomEvents initUi(props) { let uiScene = this.gameEngine.uiScene; - if(!uiScene || !sc.hasOwn(uiScene.userInterfaces, props.id)){ + if(!uiScene || !sc.hasOwn(uiScene.elementsUi, props.id)){ Logger.error('User interface not found on UI Scene: '+props.id); return false; } - let uiBox = uiScene.userInterfaces[props.id]; + let uiBox = uiScene.elementsUi[props.id]; this.uiSetTitle(uiBox, props); this.uiSetContent(uiBox, props, uiScene); let dialogContainer = uiBox.getChildByID('box-'+props.id); - dialogContainer.style.display = 'block'; + let shouldSetDisplayNone = props.keepCurrentDisplay && 'none' === dialogContainer.style.display; + dialogContainer.style.display = shouldSetDisplayNone ? 'none' : 'block'; // set box depth over the other boxes: uiBox.setDepth(2); // on dialog display clear the current target: @@ -404,26 +413,28 @@ class RoomEvents uiSetTitle(uiBox, props) { - if(!props.title){ + let newTitle = sc.get(props, 'title', false); + if(false === newTitle){ return false; } let boxTitle = uiBox.getChildByProperty('className', 'box-title'); if(!boxTitle){ return false; } - boxTitle.innerHTML = props.title; + boxTitle.innerHTML = newTitle; } uiSetContent(uiBox, props, uiScene) { - if(!props.content){ + let newContent = sc.get(props, 'content', false); + if(false === newContent){ return false; } let boxContent = uiBox.getChildByProperty('className', 'box-content'); if(!boxContent){ return false; } - boxContent.innerHTML = props.content; + boxContent.innerHTML = newContent; this.uiSetContentOptions(uiScene, props, boxContent); } @@ -509,10 +520,9 @@ class RoomEvents preloader.load.on('complete', async () => { // set ui on first preloader scene: if(!this.gameEngine.uiScene){ - // assign the preloader: this.gameEngine.uiScene = preloader; // if the box right is present then assign the actions: - this.showPlayerName(this.gameManager.playerData.name); + this.showPlayerName(this.gameManager.playerData.id + ' - ' + this.gameManager.playerData.name); } await this.createEngineScene(player, room, previousScene); }); @@ -563,7 +573,7 @@ class RoomEvents return false; } for(let i of this.playersKeysFromState(room)){ - let tmp = room.state.players.get(i); + let tmp = this.playerBySessionIdFromState(room, i); if(!tmp.sessionId || tmp.sessionId === room.sessionId){ continue; } @@ -573,24 +583,28 @@ class RoomEvents dir: tmp.state.dir, playerName: tmp.playerName, playedTime: tmp.playedTime, - avatarKey: tmp.avatarKey + avatarKey: tmp.avatarKey, + player_id: tmp.player_id }; currentScene.player.addPlayer(tmp.sessionId, addPlayerData); } } - playerByIdFromState(room, i) + playerBySessionIdFromState(room, i) { + // @TODO - BETA - Refactor and extract Colyseus into a driver. return room.state.players.get(i); } playersCountFromState(room) { + // @TODO - BETA - Refactor and extract Colyseus into a driver. return room.state.players.size; } playersKeysFromState(room) { + // @TODO - BETA - Refactor and extract Colyseus into a driver. return Array.from(room.state.players.keys()); } diff --git a/lib/game/client/scene-dynamic.js b/lib/game/client/scene-dynamic.js index b30ebf0e5..5ab395c67 100644 --- a/lib/game/client/scene-dynamic.js +++ b/lib/game/client/scene-dynamic.js @@ -166,13 +166,18 @@ class SceneDynamic extends Scene let keys = this.availableControllersKeyCodes(); let inputElements = this.gameManager.gameDom.getElements('input'); for(let inputElement of inputElements){ - this.loopKeysAddListenerToElement(keys, inputElement, 'focusin', 'removeCapture'); - this.loopKeysAddListenerToElement(keys, inputElement, 'click', 'removeCapture'); - this.loopKeysAddListenerToElement(keys, inputElement, 'focusout', 'addCapture'); - this.loopKeysAddListenerToElement(keys, inputElement, 'blur', 'addCapture'); + this.addAndRemoveCapture(keys, inputElement); } } + addAndRemoveCapture(keys, inputElement) + { + this.loopKeysAddListenerToElement(keys, inputElement, 'focusin', 'removeCapture'); + this.loopKeysAddListenerToElement(keys, inputElement, 'click', 'removeCapture'); + this.loopKeysAddListenerToElement(keys, inputElement, 'focusout', 'addCapture'); + this.loopKeysAddListenerToElement(keys, inputElement, 'blur', 'addCapture'); + } + availableControllersKeyCodes() { return [ @@ -189,29 +194,34 @@ class SceneDynamic extends Scene executePointerDownAction(pointer, currentlyOver) { + if(0 < currentlyOver.length){ + return false; + } + if(!this.gameManager.config.get('client/players/tapMovement/enabled')){ + return false; + } + if(this.gameManager.activeRoomEvents.sceneData?.worldConfig?.applyGravity){ + return false; + } let primaryMove = this.gameManager.config.get('client/ui/controls/primaryMove'); - if((!pointer.primaryDown && primaryMove) || (pointer.primaryDown && !primaryMove)){ + let primaryTouch = this.gameManager.config.get('client/ui/controls/allowPrimaryTouch'); + if( + (!pointer.wasTouch && !pointer.primaryDown && primaryMove) + || (!pointer.wasTouch && pointer.primaryDown && !primaryMove) + || (pointer.wasTouch && !pointer.primaryDown && primaryTouch) + ){ return false; } // @TODO - BETA - Temporal avoid double actions, if you target something you will not be moved to the - // pointer, in a future release this will be configurable so you can walk to objects and they get - // activated, for example, click on and NPC, automatically walk close and automatically get a dialog + // pointer, in a future release this will be configurable, so you can walk to objects and they get + // activated, for example: click on and NPC, automatically walk close and automatically get a dialog // opened. if(this.gameManager.gameDom.insideInput()){ this.gameManager.gameDom.activeElement().blur(); } - if(currentlyOver.length){ - return false; - } if(!this.appendRowAndColumn(pointer)){ return false; } - if( - !this.gameManager.config.get('client/players/tapMovement/enabled') - || this.gameManager.activeRoomEvents.sceneData?.worldConfig?.applyGravity - ){ - return false; - } this.player.moveToPointer(pointer); this.updatePointerObject(pointer); } diff --git a/lib/game/client/scene-preloader.js b/lib/game/client/scene-preloader.js index 4fd1ea716..2bc589114 100644 --- a/lib/game/client/scene-preloader.js +++ b/lib/game/client/scene-preloader.js @@ -111,6 +111,7 @@ class ScenePreloader extends Scene this.load.html('uiOptionButton', 'assets/html/ui-option-button.html'); this.load.html('uiOptionIcon', 'assets/html/ui-option-icon.html'); this.load.html('uiOptionsContainer', 'assets/html/ui-options-container.html'); + this.load.html('uiLoading', 'assets/html/ui-loading.html'); this.eventsManager.emitSync('reldens.preloadUiScene', this); } @@ -265,11 +266,7 @@ class ScenePreloader extends Scene getUiConfig(uiName, newWidth, newHeight) { let {uiX, uiY} = this.getUiPosition(uiName, newWidth, newHeight); - return { - enabled: this.gameManager.config.get('client/ui/'+uiName+'/enabled'), - uiX: uiX, - uiY: uiY - } + return {enabled: this.gameManager.config.getWithoutLogs('client/ui/'+uiName+'/enabled'), uiX, uiY} } getUiPosition(uiName, newWidth, newHeight) @@ -277,26 +274,28 @@ class ScenePreloader extends Scene if('' === uiName){ uiName = 'default'; } - let uiX = this.gameManager.config.get('client/ui/'+uiName+'/x'); - let uiY = this.gameManager.config.get('client/ui/'+uiName+'/y'); + let uiConfig = this.gameManager.config.getWithoutLogs('client/ui/'+uiName, {}); + let uiX = sc.get(uiConfig, 'x', 0); + let uiY = sc.get(uiConfig, 'y', 0); if(this.gameManager.config.get('client/ui/screen/responsive')){ - let rX = this.gameManager.config.get('client/ui/'+uiName+'/responsiveX'); - let rY = this.gameManager.config.get('client/ui/'+uiName+'/responsiveY'); + let rX = sc.get(uiConfig, 'responsiveX', false); + let rY = sc.get(uiConfig, 'responsiveY', false); + let gameContainer = this.gameManager.gameDom.getElement('.game-container'); if(!newWidth){ - newWidth = this.gameManager.gameDom.getElement('.game-container').offsetWidth; + newWidth = gameContainer.offsetWidth; } if(!newHeight){ - newHeight = this.gameManager.gameDom.getElement('.game-container').offsetHeight; + newHeight = gameContainer.offsetHeight; } - uiX = rX ? rX * newWidth / 100 : 0; - uiY = rY ? rY * newHeight / 100 : 0; + uiX = false !== rX ? rX * newWidth / 100 : 0; + uiY = false !== rY ? rY * newHeight / 100 : 0; } return {uiX, uiY}; } preloadPlayerDefaultSprite() { - let fallbackImage = this.gameManager.config.get('client/players/animations/fallbackImage') || 'player-base'; + let fallbackImage = this.gameManager.config.get('client/players/animations/fallbackImage', 'player-base'); this.load.spritesheet( GameConst.IMAGE_PLAYER, 'assets/custom/sprites/'+fallbackImage+'.png', @@ -465,7 +464,7 @@ class ScenePreloader extends Scene type: action.type }; } - this.repeatHold(dataSend); + this.gameManager.activeRoomEvents.room.send('*', dataSend); } endHold(event, button) @@ -478,14 +477,6 @@ class ScenePreloader extends Scene this.gameManager.activeRoomEvents.room.send('*', {act: GameConst.STOP}); } - repeatHold(sendData) - { - this.gameManager.activeRoomEvents.room.send('*', sendData); - this.holdTimer = setTimeout(() => { - this.repeatHold(sendData); - }, (this.timeout || 0)); - } - createProgressBar() { let Rectangle = Geom.Rectangle; diff --git a/lib/game/client/settings-ui.js b/lib/game/client/settings-ui.js index 1cde7a2a6..24435257f 100644 --- a/lib/game/client/settings-ui.js +++ b/lib/game/client/settings-ui.js @@ -9,6 +9,7 @@ class SettingsUi createSettings(settingsConfig, scenePreloader) { + // @TODO - BETA - Replace by UserInterface. scenePreloader.elementsUi['settings'] = scenePreloader.add.dom(settingsConfig.uiX, settingsConfig.uiY) .createFromCache('settings'); let settingsTemplate = scenePreloader.cache.html.get('settings-content'); diff --git a/lib/game/client/ui-factory.js b/lib/game/client/ui-factory.js new file mode 100644 index 000000000..eeded5ea5 --- /dev/null +++ b/lib/game/client/ui-factory.js @@ -0,0 +1,54 @@ +/** + * + * Reldens - UiFactory + * + */ + +const { GameConst } = require('../constants'); + +class UiFactory +{ + + constructor(uiScene) + { + this.uiScene = uiScene; + this.gameManager = this.uiScene.gameManager; + } + + create(uiCodeName, depth, defaultOpen, defaultClose, openCallback, closeCallback) + { + // @TODO - BETA - Replace by UserInterface. + let {uiX, uiY} = this.uiScene.getUiConfig(uiCodeName); + let newUiObject = this.uiScene.add.dom(uiX, uiY).createFromCache(uiCodeName); + let openButton = newUiObject.getChildByProperty('id', uiCodeName+GameConst.UI_OPEN); + let closeButton = newUiObject.getChildByProperty('id', uiCodeName+GameConst.UI_CLOSE); + openButton?.addEventListener('click', () => { + if(defaultOpen){ + let box = newUiObject.getChildByProperty('id', uiCodeName+'-ui'); + box.style.display = 'block'; + openButton.style.display = 'none'; + newUiObject.setDepth(depth); + } + if(openCallback && 'function' === typeof (openCallback)){ + openCallback(); + } + }); + closeButton?.addEventListener('click', () => { + if(defaultClose){ + let box = newUiObject.getChildByProperty('id', uiCodeName+'-ui'); + box.style.display = 'none'; + newUiObject.setDepth(1); + if(openButton){ + openButton.style.display = 'block'; + } + } + if(closeCallback && 'function' === typeof (closeCallback)){ + closeCallback(); + } + }); + this.uiScene.elementsUi[uiCodeName] = newUiObject; + } + +} + +module.exports.UiFactory = UiFactory; diff --git a/lib/game/client/user-interface.js b/lib/game/client/user-interface.js index eeb076f7e..37347b900 100644 --- a/lib/game/client/user-interface.js +++ b/lib/game/client/user-interface.js @@ -7,6 +7,7 @@ */ const { GameConst } = require('../constants'); +const { Logger, sc } = require('@reldens/utils'); class UserInterface { @@ -30,6 +31,9 @@ class UserInterface preloadUiElement(preloadScene) { + if(!this.template){ + return; + } preloadScene.load.html(this.id, this.template); } @@ -39,32 +43,97 @@ class UserInterface templateKey = this.id; } let objectElementId = 'box-'+this.id; - let exists = uiScene.gameManager.gameDom.getElement('#'+objectElementId); - if(exists){ - // avoid duplicated elements: - return true; + let gameDom = uiScene.gameManager.gameDom; + if(sc.get(uiScene.elementsUi, this.id)){ + return this; + } + let dialogBox = this.createDialogBox(uiScene, templateKey); + this.createBoxContent(uiScene, templateKey, dialogBox); + let dialogContainer = gameDom.getElement('.ui-box.ui-dialog-box', dialogBox.node); + if(!dialogContainer){ + Logger.critical('Missing dialog container for template key: "'+templateKey+'".', { + dialogBox, + dialogContainer, + objectElementId + }); + return false; } + dialogContainer.id = objectElementId; + dialogContainer.classList.add('type-'+(this.animProps?.type || 'dialog-box')); + let openButton = this.activateOpenButton(dialogBox, dialogContainer, gameDom); + this.activateCloseButton(dialogBox, dialogContainer, openButton, uiScene, gameDom); + uiScene.userInterfaces[this.id] = this; + uiScene.elementsUi[this.id] = dialogBox; + // @TODO - BETA - refactor to return the created dialog box. + return this; + } + + createDialogBox(uiScene, templateKey) + { let {newWidth, newHeight} = uiScene.gameManager.gameEngine.getCurrentScreenSize(uiScene.gameManager); let {uiX, uiY} = uiScene.getUiPosition(this.uiPositionKey, newWidth, newHeight); - let dialogBox = uiScene.add.dom(uiX, uiY).createFromCache(templateKey); + return uiScene.add.dom(uiX, uiY).createFromCache(templateKey); + } + + createBoxContent(uiScene, templateKey, dialogBox) + { let messageTemplate = uiScene.cache.html.get(templateKey); dialogBox.innerHTML = uiScene.gameManager.gameEngine.parseTemplate(messageTemplate, { title: this.initialTitle, content: this.initialContent }); - let dialogContainer = dialogBox.getChildByProperty('className', 'ui-box ui-dialog-box'); - dialogContainer.id = objectElementId; - dialogContainer.classList.add('type-'+(this.animProps?.type || 'dialog-box')); - let boxClose = dialogBox.getChildByProperty('className', 'box-close'); - if(boxClose){ - boxClose.id = 'box-close-' + this.id; - boxClose.addEventListener('click', () => { - dialogContainer.style.display = 'none'; - uiScene.gameManager.activeRoomEvents.room.send('*', {act: GameConst.CLOSE_UI_ACTION, id: this.id}); - }); + } + + activateOpenButton(dialogBox, dialogContainer, gameDom) + { + // @TODO - BETA - Extract into a new service. + let openButton = gameDom.getElement('.'+GameConst.UI_BOX + GameConst.UI_OPEN, dialogBox.node); + if(!openButton){ + return false; + } + openButton.id = GameConst.UI_BOX + GameConst.UI_OPEN + '-' + this.id + openButton.addEventListener('click', () => { + if(sc.get(this.animProps, 'defaultOpen', true)){ + dialogContainer.style.display = 'block'; + openButton.style.display = 'none'; + if(false !== sc.get(this.animProps, 'depth', false)){ + dialogBox.setDepth(this.animProps.depth); + } + } + if(sc.isFunction(this.animProps['openCallBack'])){ + this.animProps['openCallBack'](); + } + }); + return openButton; + } + + activateCloseButton(dialogBox, dialogContainer, openButton, uiScene, gameDom) + { + // @TODO - BETA - Extract into a new service. + let closeButton = gameDom.getElement('.'+GameConst.UI_BOX + GameConst.UI_CLOSE, dialogBox.node); + if(!closeButton){ + return false; } - uiScene.userInterfaces[this.id] = dialogBox; + closeButton.id = GameConst.UI_BOX + GameConst.UI_CLOSE + '-' + this.id; + closeButton.addEventListener('click', () => { + if(!sc.hasOwn(this.animProps, 'sendCloseMessage') || false === this.animProps['sendCloseMessage']){ + uiScene.gameManager.activeRoomEvents.room.send('*', {act: GameConst.CLOSE_UI_ACTION, id: this.id}); + } + if(sc.get(this.animProps, 'defaultClose', true)){ + dialogContainer.style.display = 'none'; + if(openButton){ + openButton.style.display = 'block'; + } + if(false !== sc.get(this.animProps, 'depth', false)){ + dialogBox.setDepth(1); + } + } + if(sc.isFunction(this.animProps['closeCallback'])){ + this.animProps['closeCallback'](); + } + }); } + } module.exports.UserInterface = UserInterface; diff --git a/lib/game/constants.js b/lib/game/constants.js index 521d5508b..7c5da5080 100644 --- a/lib/game/constants.js +++ b/lib/game/constants.js @@ -2,9 +2,6 @@ * * Reldens - game/constants * - * Project constants and default data. - * Here we use shortcuts since these are used for all the communications between server and client. - * */ module.exports.GameConst = { @@ -24,7 +21,10 @@ module.exports.GameConst = { GAME_OVER: 'go', REVIVED: 'rv', BUTTON_OPTION: 'btn-opt', - // movement: + UI_BOX: 'box', + UI_CLOSE: '-close', + UI_OPEN: '-open', + CANVAS: 'CANVAS', UP: 'up', LEFT: 'left', DOWN: 'down', @@ -32,7 +32,6 @@ module.exports.GameConst = { STOP: 'stop', POINTER: 'mp', ARROW_DOWN: 'ard', - // default image key: IMAGE_PLAYER: 'player', STATUS: { ACTIVE: 1, @@ -40,7 +39,7 @@ module.exports.GameConst = { DEATH: 3, AVOID_INTERPOLATION: 4 }, - THEMES: { + STRUCTURE: { DEFAULT: 'default', ASSETS: 'assets', CSS: 'css', @@ -53,5 +52,18 @@ module.exports.GameConst = { CSS_FILE: 'styles.css', ADMIN_SCSS_FILE: 'reldens-admin.scss', ADMIN_CSS_FILE: 'reldens-admin.css' + }, + LABELS: { + YES: 'Yes', + NO: 'No' + }, + GRAPHICS: { + FRAME_WIDTH: 32, + FRAME_HEIGHT: 32 + }, + SHOW_PLAYER_TIME: { + NONE: -1, + ONLY_OWN_PLAYER: 0, + ALL_PLAYERS: 2, } }; diff --git a/lib/game/server/app-server.js b/lib/game/server/app-server-factory.js similarity index 81% rename from lib/game/server/app-server.js rename to lib/game/server/app-server-factory.js index da05b34e8..0fc9e7942 100644 --- a/lib/game/server/app-server.js +++ b/lib/game/server/app-server-factory.js @@ -1,6 +1,6 @@ /** * - * Reldens - AppServer + * Reldens - AppServerFactory * */ @@ -10,19 +10,15 @@ const fs = require('fs'); const express = require('express'); const cors = require('cors'); -class AppServer +class AppServerFactory { - createAppServer(distPath) + createAppServer() { let appServer = false; let app = express(); app.use(cors()); app.use(express.json()); - if(process.env.RELDENS_EXPRESS_SERVE_STATICS){ - // automatically serve dist files: - app.use('/', express.static(distPath)); - } // if https is not running then by default we will run on http: appServer = process.env.RELDENS_EXPRESS_USE_HTTPS ? this.createHttpsServer(appServer, app) @@ -46,6 +42,11 @@ class AppServer return https.createServer(credentials, app); } + enableServeStatics(app, distPath) + { + app.use('/', express.static(distPath)); + } + } -module.exports.AppServer = new AppServer(); +module.exports.AppServerFactory = new AppServerFactory(); diff --git a/lib/game/server/entity-properties.js b/lib/game/server/entity-properties.js index 849ff5e45..c84fd65a5 100644 --- a/lib/game/server/entity-properties.js +++ b/lib/game/server/entity-properties.js @@ -11,12 +11,12 @@ class EntityProperties static propertiesDefinition() { - Logger.error('Method not implemented propertiesDefinition().'); + Logger.alert('Method not implemented propertiesDefinition().'); } static propertiesConfig(extraProps) { - Logger.error('Method not implemented propertiesConfig().', extraProps); + Logger.alert('Method not implemented propertiesConfig().', extraProps); } } diff --git a/lib/game/server/login-manager.js b/lib/game/server/login-manager.js index bdfd974ee..5da8940ed 100644 --- a/lib/game/server/login-manager.js +++ b/lib/game/server/login-manager.js @@ -31,7 +31,18 @@ class LoginManager { this.events.on('reldens.serverBeforeListen', (props) => { props.serverManager.app.get('/reldens-mailer-enabled', (req, res) => { - res.json({enabled: this.mailer.isEnabled()}); + res.json({ + enabled: this.mailer.isEnabled() + }); + }); + props.serverManager.app.get('/terms-and-conditions', (req, res) => { + let termsConfig = this.config.getWithoutLogs('client/login/termsAndConditions', {}); + res.json({ + link: sc.get(termsConfig, 'link', ''), + heading: sc.get(termsConfig, 'heading', ''), + body: sc.get(termsConfig, 'body', ''), + checkboxLabel: sc.get(termsConfig, 'checkboxLabel', '') + }); }); }); } @@ -56,14 +67,16 @@ class LoginManager this.events.emitSync('reldens.invalidData', this, userData, result); return result; } - // search if the user exists: - let user = await this.usersManager.loadUserByUsername(userData.username); - if(!user && !userData.isNewUser){ - let result = {error: 'Missing user data.'}; - this.events.emitSync('reldens.loadUserByUsernameResult', this, userData, result); - return result; + let user = await this.usersManager.fetchUserByNameOrEmail(userData.username, userData.email); + if(user){ + return await this.login(user, userData); } - return user ? await this.login(user, userData) : await this.register(userData); + if(userData.isNewUser){ + return await this.register(userData); + } + let result = {error: 'Invalid user data.'}; + this.events.emitSync('reldens.loginInvalidParams', this, user, userData, result); + return result; } isValidData(userData) @@ -80,7 +93,7 @@ class LoginManager { // check if the passwords match: if(!this.passwordManager.validatePassword(userData.password, user.password)){ - let result = {error: 'User already exists or invalid user data.'}; + let result = {error: 'Invalid user data.'}; this.events.emitSync('reldens.loginInvalidPassword', this, user, userData, result); return result; } @@ -151,20 +164,12 @@ class LoginManager async register(userData) { if(!userData.isNewUser){ - let result = {error: 'Unable to authenticate the user.'}; + let result = {error: 'Registration error, there was an error with your request, please try again later.'}; + Logger.error('Registration invalida parameters.', userData); await this.events.emit('reldens.register', this, userData, result); return result; } try { - // check if a user with the email exists: - let user = await this.usersManager.loadUserByEmail(userData.email); - if(user){ - let message = 'Registration error, invalid credentials.'; - if(userData.isFirebaseLogin){ - message = 'Login error, wrong username.'; - } - return {error: message}; - } // if the email doesn't exist in the database, and it's a registration request: // insert user, player, player state, player stats, class path: let newUser = await this.usersManager.createUser({ @@ -198,10 +203,10 @@ class LoginManager state: initialState }; await this.events.emit('reldens.createNewPlayerBefore', loginData, playerData, this); - let isUnavailable = await this.usersManager.isNameAvailable(playerData.name); - if(isUnavailable){ + let isTaken = await this.usersManager.isNameAvailable(playerData.name); + if(isTaken){ let result = {error: true, message: 'The player name is not available, please choose another name.'}; - await this.events.emit('reldens.playerNewNameUnavailable', this, loginData, isUnavailable, result); + await this.events.emit('reldens.playerNewNameUnavailable', this, loginData, isTaken, result); return result; } try { diff --git a/lib/game/server/manager.js b/lib/game/server/manager.js index f6df97bfb..1508920e7 100644 --- a/lib/game/server/manager.js +++ b/lib/game/server/manager.js @@ -9,8 +9,9 @@ const dotenv = require('dotenv'); const path = require('path'); +const ReldensASCII = require('../reldens-ascii'); const { GameServer } = require('./game-server'); -const { AppServer } = require('./app-server'); +const { AppServerFactory } = require('./app-server-factory'); const { ConfigManager } = require('../../config/server/manager'); const { DataServerInitializer } = require('./data-server-initializer'); const { FeaturesManager } = require('../../features/server/manager'); @@ -21,7 +22,6 @@ const { Mailer } = require('./mailer'); const { ThemeManager } = require('./theme-manager'); const { MapsLoader } = require('./maps-loader'); const { ForgotPassword } = require('./forgot-password'); -const ReldensASCII = require('../reldens-ascii'); const { EventsManagerSingleton, Logger, sc } = require('@reldens/utils'); const { WebSocketTransport } = require('@colyseus/ws-transport'); @@ -49,21 +49,16 @@ class ServerManager { this.events = eventsManager || EventsManagerSingleton; try { - // custom plugin goes even before config to catch up every single event: + // @NOTE: custom plugin goes even before config to catch up on every single event. this.setupCustomServerPlugin(config); - // initialize configurations: this.initializeConfiguration(config); - // initialize theme: this.themeManager = new ThemeManager(config); this.themeManager.validateOrCreateTheme(); - // initialize storage: this.initializeStorage(config, dataServerDriver).catch((error) => { - Logger.error('Storage could not be initialized.', error); + Logger.critical('Storage could not be initialized.', error); process.exit(); }); - // set storage driver on configuration manager: this.configManager.dataServer = this.dataServer; - // load maps: MapsLoader.loadMaps(this.themeManager.projectThemePath, this.configManager); } catch (e) { Logger.error('Broken ServerManager.', e.message, e.stack); @@ -72,19 +67,6 @@ class ServerManager } } - async initializeStorage(config, dataServerDriver) - { - let {dataServerConfig, dataServer} = DataServerInitializer.initializeEntitiesAndDriver({ - config, - dataServerDriver, - serverManager: this - }); - this.dataServerConfig = dataServerConfig; - this.dataServer = dataServer; - await dataServer.connect(); // can't auto-connect on the constructor - await dataServer.generateEntities(); - } - setupCustomServerPlugin(config) { let customPluginClass = sc.get(config, 'customPlugin', false); @@ -122,40 +104,86 @@ class ServerManager this.isHotPlugEnabled = process.env.RELDENS_HOT_PLUG || false; } + async initializeStorage(config, dataServerDriver) + { + let {dataServerConfig, dataServer} = DataServerInitializer.initializeEntitiesAndDriver({ + config, + dataServerDriver, + serverManager: this + }); + this.dataServerConfig = dataServerConfig; + this.dataServer = dataServer; + await dataServer.connect(); // can't auto-connect on the constructor + await dataServer.generateEntities(); + } + async start() { - Logger.info('Starting Server Manager!'); - await this.createServer(); - await this.createGameServer(); + Logger.info('Starting Server!'); + if(!this.gameServer){ + Logger.critical('App Server is not defined.'); + return false; + } + if(!this.gameServer){ + Logger.critical('Game Server is not defined.'); + return false; + } await this.initializeManagers(); // after the rooms were loaded then finish the server process: await this.events.emit('reldens.serverBeforeListen', {serverManager: this}); await this.gameServer.listen(this.configServer.port); this.configManager.configList.server.baseUrl = this.configServer.host+':'+this.configServer.port; - await this.createClientBundle(); Logger.info('Server ready.'+ReldensASCII); Logger.info('Server listening on '+this.configServer.host+':'+this.configServer.port); await this.events.emit('reldens.serverReady', {serverManager: this}); } - async createServer() + async createServers() + { + await this.createAppServer(); + this.enableServeStatics(); + await this.createGameServer(); + } + + async createAppServer() + { + // @TODO - BETA - Pass AppServerFactory to the ServerManager constructor to avoid the other libraries require + // if are not needed. Modify theme/index.js.dist to pass it on the default installation. + let event = {serverManager: this, continueProcess: true}; + await this.events.emit('reldens.createAppServer', event); + if(!event.continueProcess){ + return false; + } + Object.assign(this, AppServerFactory.createAppServer()); + } + + enableServeStatics() { - await this.events.emit('reldens.serverStartBegin', {serverManager: this}); - Object.assign(this, AppServer.createAppServer(this.themeManager.distPath)); + if(!process.env.RELDENS_EXPRESS_SERVE_STATICS){ + return false; + } + AppServerFactory.enableServeStatics(this.app, this.themeManager.distPath); } async createGameServer() { - // create game server instance: - // this.gameServer = new GameServer({server: this.appServer, express: this.app}); + // @TODO - BETA - Extract into a GameServerFactory, pass it to the ServerManager constructor to avoid the other + // libraries require if are not needed. Modify theme/index.js.dist to pass it on the default installation. + let options = { + pingInterval: process.env.RELDENS_PING_INTERVAL || 5000, + pingMaxRetries: process.env.RELDENS_PING_MAX_RETRIES || 3 + }; + if(this.appServer){ + options.server = this.appServer; + } + let event = {options, continueProcess: true}; + await this.events.emit('reldens.createGameServer', event); + if(!event.continueProcess){ + return false; + } this.gameServer = new GameServer({ - transport: new WebSocketTransport({ - pingInterval: 5000, - pingMaxRetries: 3, - server: this.appServer - }) + transport: new WebSocketTransport(options) }); - // attach web monitoring panel (optional): if(this.configServer.monitor.enabled){ this.gameServer.attachMonitor(this.app, this.configServer.monitor); } @@ -163,32 +191,30 @@ class ServerManager async initializeManagers() { - // get config processor instance: await this.configManager.loadConfigurations(); - // save project paths in config: this.configManager.projectPaths = this.themeManager.paths(); await this.events.emit('reldens.serverConfigReady', { serverManager: this, configProcessor: this.configManager }); - // mailer: + // @TODO - BETA - Extract and pass to the ServerManager in the constructor. this.mailer = new Mailer(); Logger.info(['Mailer Configured:', this.mailer.isEnabled()]); await ForgotPassword.defineRequestOnServerManagerApp(this); - // features manager: - this.featuresManager = new FeaturesManager({events: this.events, dataServer: this.dataServer}); - // load the available features list and append to the config, this way we will pass the list to the client: + this.featuresManager = new FeaturesManager({ + events: this.events, + dataServer: this.dataServer, + config: this.configManager + }); this.configManager.availableFeaturesList = await this.featuresManager.loadFeatures(); await this.events.emit('reldens.serverConfigFeaturesReady', { serverManager: this, configProcessor: this.configManager }); - // users manager: this.usersManager = new UsersManager({events: this.events, dataServer: this.dataServer}); // the "rooms" manager will receive the features rooms to be defined: this.roomsManager = new RoomsManager({events: this.events, dataServer: this.dataServer}); await this.events.emit('reldens.serverBeforeLoginManager', {serverManager: this}); - // login manager: this.loginManager = new LoginManager({ config: this.configManager, usersManager: this.usersManager, @@ -197,7 +223,6 @@ class ServerManager themeManager: this.themeManager, events: this.events }); - // prepare rooms: await this.events.emit('reldens.serverBeforeDefineRooms', {serverManager: this}); await this.roomsManager.defineRoomsInGameServer(this.gameServer, { loginManager: this.loginManager, @@ -206,23 +231,6 @@ class ServerManager }); } - async createClientBundle() - { - // @TODO - BETA - Remove this function, just move to an auto-install on first run feature. - let runBundler = process.env.RELDENS_PARCEL_RUN_BUNDLER || false; - if(!runBundler){ - return false; - } - if(process.env.RELDENS_ON_BUNDLE_RESET_DIST){ - await this.themeManager.resetDist(); - } - if(process.env.RELDENS_ON_BUNDLE_RESET_DIST || process.env.RELDENS_ON_BUNDLE_COPY_ASSETS){ - await this.themeManager.copyAssetsToDist(); - } - Logger.info('Running bundle on: ' + this.themeManager.projectIndexPath); - await this.themeManager.buildClient(); - } - } module.exports.ServerManager = ServerManager; diff --git a/lib/game/server/theme-manager.js b/lib/game/server/theme-manager.js index 988d125b7..ffd971fd0 100644 --- a/lib/game/server/theme-manager.js +++ b/lib/game/server/theme-manager.js @@ -27,7 +27,7 @@ class ThemeManager assetsDistPath = ''; cssDistPath = ''; themePath = ''; - projectThemeName = GameConst.THEMES.DEFAULT; + projectThemeName = GameConst.STRUCTURE.DEFAULT; projectThemePath = ''; projectPluginsPath = ''; projectAssetsPath = ''; @@ -46,22 +46,22 @@ class ThemeManager { this.projectRoot = props.projectRoot; this.projectRootPackageJson = path.join(this.projectRoot, 'package.json'); - this.projectThemeName = sc.get(props, 'projectThemeName', GameConst.THEMES.DEFAULT); + this.projectThemeName = sc.get(props, 'projectThemeName', GameConst.STRUCTURE.DEFAULT); this.reldensModulePath = path.join(this.projectRoot, 'node_modules', 'reldens'); - this.reldensModuleLibPath = path.join(this.reldensModulePath, GameConst.THEMES.LIB); - this.reldensModuleThemePath = path.join(this.reldensModulePath, GameConst.THEMES.THEME); - this.reldensModuleDefaultThemePath = path.join(this.reldensModuleThemePath, GameConst.THEMES.DEFAULT); - this.reldensModuleDefaultThemeAssetsPath = path.join(this.reldensModuleDefaultThemePath, GameConst.THEMES.ASSETS); - this.reldensModuleThemePluginsPath = path.join(this.reldensModuleThemePath, GameConst.THEMES.PLUGINS); - this.distPath = path.join(this.projectRoot, GameConst.THEMES.DIST); - this.assetsDistPath = path.join(this.distPath, GameConst.THEMES.ASSETS); - this.cssDistPath = path.join(this.distPath, GameConst.THEMES.CSS); - this.themePath = path.join(this.projectRoot, GameConst.THEMES.THEME); + this.reldensModuleLibPath = path.join(this.reldensModulePath, GameConst.STRUCTURE.LIB); + this.reldensModuleThemePath = path.join(this.reldensModulePath, GameConst.STRUCTURE.THEME); + this.reldensModuleDefaultThemePath = path.join(this.reldensModuleThemePath, GameConst.STRUCTURE.DEFAULT); + this.reldensModuleDefaultThemeAssetsPath = path.join(this.reldensModuleDefaultThemePath, GameConst.STRUCTURE.ASSETS); + this.reldensModuleThemePluginsPath = path.join(this.reldensModuleThemePath, GameConst.STRUCTURE.PLUGINS); + this.distPath = path.join(this.projectRoot, GameConst.STRUCTURE.DIST); + this.assetsDistPath = path.join(this.distPath, GameConst.STRUCTURE.ASSETS); + this.cssDistPath = path.join(this.distPath, GameConst.STRUCTURE.CSS); + this.themePath = path.join(this.projectRoot, GameConst.STRUCTURE.THEME); this.projectThemePath = path.join(this.themePath, this.projectThemeName); - this.projectPluginsPath = path.join(this.themePath, GameConst.THEMES.PLUGINS); - this.projectAssetsPath = path.join(this.projectThemePath, GameConst.THEMES.ASSETS); - this.projectCssPath = path.join(this.projectThemePath, GameConst.THEMES.CSS); - this.projectIndexPath = path.join(this.projectThemePath, GameConst.THEMES.INDEX); + this.projectPluginsPath = path.join(this.themePath, GameConst.STRUCTURE.PLUGINS); + this.projectAssetsPath = path.join(this.projectThemePath, GameConst.STRUCTURE.ASSETS); + this.projectCssPath = path.join(this.projectThemePath, GameConst.STRUCTURE.CSS); + this.projectIndexPath = path.join(this.projectThemePath, GameConst.STRUCTURE.INDEX); } paths() @@ -205,7 +205,7 @@ class ThemeManager async buildCss() { - let themeScss = path.join(this.projectCssPath, GameConst.THEMES.SCSS_FILE).toString(); + let themeScss = path.join(this.projectCssPath, GameConst.STRUCTURE.SCSS_FILE).toString(); let bundler = this.createCssBundler(themeScss); try { let { buildTime } = await bundler.run(); @@ -218,9 +218,9 @@ class ThemeManager async buildAdminCss() { - let adminCss = path.join(this.projectCssPath, GameConst.THEMES.ADMIN_CSS_FILE); + let adminCss = path.join(this.projectCssPath, GameConst.STRUCTURE.ADMIN_CSS_FILE); if(!fs.existsSync(adminCss)){ - let adminScss = path.join(this.projectCssPath, GameConst.THEMES.ADMIN_SCSS_FILE); + let adminScss = path.join(this.projectCssPath, GameConst.STRUCTURE.ADMIN_SCSS_FILE); let bundler = this.createCssBundler(adminScss); try { let { buildTime } = await bundler.run(); @@ -230,7 +230,7 @@ class ThemeManager ErrorManager.error('Parcel build Game Client process failed.'); } } - let adminCssDist = path.join(this.cssDistPath, GameConst.THEMES.ADMIN_CSS_FILE); + let adminCssDist = path.join(this.cssDistPath, GameConst.STRUCTURE.ADMIN_CSS_FILE); if(!fs.existsSync(this.cssDistPath)){ fs.mkdirSync(this.cssDistPath); } @@ -360,6 +360,23 @@ class ThemeManager return TemplateEngine.render(fileContent, params); } + async createClientBundle() + { + // @TODO - BETA - Remove this function, just move to an auto-installation on first run feature. + let runBundler = process.env.RELDENS_PARCEL_RUN_BUNDLER || false; + if(!runBundler){ + return false; + } + if(process.env.RELDENS_ON_BUNDLE_RESET_DIST){ + await this.resetDist(); + } + if(process.env.RELDENS_ON_BUNDLE_RESET_DIST || process.env.RELDENS_ON_BUNDLE_COPY_ASSETS){ + await this.copyAssetsToDist(); + } + Logger.info('Running bundle on: ' + this.projectIndexPath); + await this.buildClient(); + } + } module.exports.ThemeManager = ThemeManager; diff --git a/lib/inventory/client/exchange/trade-target-action.js b/lib/inventory/client/exchange/trade-target-action.js index 3be3b3b4a..6ca98c2df 100644 --- a/lib/inventory/client/exchange/trade-target-action.js +++ b/lib/inventory/client/exchange/trade-target-action.js @@ -6,7 +6,7 @@ const { InventoryConst } = require('../../constants'); const { GameConst } = require('../../../game/constants'); -const { sc } = require('@reldens/utils'); +const { Logger, sc } = require('@reldens/utils'); class TradeTargetAction { @@ -22,15 +22,26 @@ class TradeTargetAction return false; } let inventoryTradeStartTemplate = uiScene.cache.html.get('inventoryTradeStart'); - uiTarget.getChildByID('box-target').style.display = 'block'; - uiTarget.getChildByID('target-container').innerHTML += gameManager.gameEngine.parseTemplate( - inventoryTradeStartTemplate, - { - playerName: targetName, - playerId: target.id - } + if(!inventoryTradeStartTemplate){ + Logger.critical('Template "inventoryTradeStart" not found.'); + return false; + } + gameManager.gameDom.appendToElement( + '#target-container', + gameManager.gameEngine.parseTemplate( + inventoryTradeStartTemplate, + { + playerName: targetName, + playerId: target.id + } + ) ); - gameManager.gameDom.getElement('.start-trade-'+target.id+' button')?.addEventListener('click', () => { + let tradeStartButton = gameManager.gameDom.getElement('.start-trade-'+target.id+' button'); + if(!tradeStartButton){ + Logger.critical('Trade start button not found for selector: "'+'.start-trade-'+target.id+' button'+'"'); + return false; + } + tradeStartButton.addEventListener('click', () => { let sendData = {act: InventoryConst.ACTIONS.TRADE_START, id: target.id}; gameManager.room.send('*', sendData); }); diff --git a/lib/inventory/client/inventory-ui.js b/lib/inventory/client/inventory-ui.js index 90960b9ac..91d770a6b 100644 --- a/lib/inventory/client/inventory-ui.js +++ b/lib/inventory/client/inventory-ui.js @@ -2,56 +2,36 @@ * * Reldens - InventoryUi * - * This class will handle the inventory UI and assign all the related events and actions. - * */ +const { UiFactory } = require('../../game/client/ui-factory'); const { InventoryConst } = require('../constants'); -class InventoryUi +class InventoryUi extends UiFactory { - constructor(uiScene) - { - this.uiScene = uiScene; - this.gameManager = this.uiScene.gameManager; - } - createUi() { - this.create('inventory', 5); - this.create('equipment', 4); + // @TODO - BETA - Replace by UserInterface. + this.create('inventory', 5, true, true, null, () => { + this.inventoryVisibility('inventory'); + }); + this.create('equipment', 4, true, true, null, () => { + this.inventoryVisibility('inventory'); + }); } - create(codeName, depth) + inventoryVisibility(constantCodeName) { - let consName = codeName.toUpperCase(); - let {uiX, uiY} = this.uiScene.getUiConfig(codeName); - let newUiObject = this.uiScene.add.dom(uiX, uiY).createFromCache(codeName); - let closeButton = newUiObject.getChildByProperty('id', InventoryConst[consName+'_CLOSE']); - let openButton = newUiObject.getChildByProperty('id', InventoryConst[consName+'_OPEN']); - closeButton?.addEventListener('click', () => { - let box = newUiObject.getChildByProperty('id', codeName+'-ui'); - box.style.display = 'none'; - let uiPanel = newUiObject.getChildByProperty('id', InventoryConst[consName+'_ITEMS']); - uiPanel.querySelectorAll('.item-box .image-container img').forEach(function(element){ - element.style.border = 'none'; - }); - uiPanel.querySelectorAll('.item-data-container').forEach(function(element){ - element.style.display = 'none'; - }); - if(openButton){ - openButton.style.display = 'block'; - } - newUiObject.setDepth(1); - }); - openButton?.addEventListener('click', () => { - let box = newUiObject.getChildByProperty('id', codeName+'-ui'); - box.style.display = 'block'; - openButton.style.display = 'none'; - newUiObject.setDepth(depth); - }); - this.uiScene.elementsUi[codeName] = newUiObject; + let containerId = '#'+InventoryConst[constantCodeName+'_ITEMS']; + let itemImages = this.gameManager.gameDom.getElements(containerId+' .item-box .image-container img'); + for(let itemImage of itemImages){ + itemImage.style.border = 'none'; + } + let itemContainers = this.gameManager.gameDom.getElements(containerId+' .item-data-container') + for(let itemContainer of itemContainers){ + itemContainer.style.border = 'none'; + } } } diff --git a/lib/inventory/client/plugin.js b/lib/inventory/client/plugin.js index 6ec394c1c..b24925759 100644 --- a/lib/inventory/client/plugin.js +++ b/lib/inventory/client/plugin.js @@ -11,6 +11,7 @@ const { TradeTargetAction } = require('./exchange/trade-target-action'); const { TradeMessageListener } = require('./trade-message-listener'); const { UserInterface } = require('../../game/client/user-interface'); const { PluginInterface } = require('../../features/plugin-interface'); +const { TemplatesHandler } = require('./templates-handler'); const { InventoryConst } = require('../constants'); const { Logger, sc } = require('@reldens/utils'); @@ -19,12 +20,8 @@ class InventoryPlugin extends PluginInterface setup(props) { - // @TODO - Refactor plugin, extract all the methods into new classes. + // @TODO - BETA - Refactor plugin, extract all the methods into new classes. this.gameManager = sc.get(props, 'gameManager', false); - this.tradeTargetAction = new TradeTargetAction(); - // @TODO - Make the dialogBox template load on it's own so we can use the same object from cache everytime. - // @NOTE: the tradeUi works as preload for the trade template which at the end is an dialog-box. - this.tradeUi = new UserInterface(this.gameManager, {id: 'trade', type: 'trade'}); if(!this.gameManager){ Logger.error('Game Manager undefined in InventoryPlugin.'); } @@ -32,11 +29,15 @@ class InventoryPlugin extends PluginInterface if(!this.events){ Logger.error('EventsManager undefined in InventoryPlugin.'); } + this.tradeTargetAction = new TradeTargetAction(); + // @TODO - BETA - Make the dialogBox template load on it's own so we can reuse the same object from cache. + // @NOTE: the tradeUi works as preload for the trade template which at the end is an dialog-box. + this.tradeUi = new UserInterface(this.gameManager, {id: 'trade', type: 'trade'}); this.events.on('reldens.playersOnAdd', (player, key, previousScene, roomEvents) => { this.onPlayerAdd(key, roomEvents, player); }); this.events.on('reldens.preloadUiScene', (preloadScene) => { - this.preloadTemplates(preloadScene); + TemplatesHandler.preloadTemplates(preloadScene); }); this.events.on('reldens.createUiScene', (preloadScene) => { return this.onPreloadUiScene(preloadScene); @@ -47,55 +48,41 @@ class InventoryPlugin extends PluginInterface this.gameManager.config.client.message.listeners['trade'] = new TradeMessageListener(); } - preloadTemplates(preloadScene) - { - // @TODO - BETA - Replace by loader replacing snake name file name by camel case for the template key. - let inventoryTemplatePath = 'assets/features/inventory/templates/'; - // @TODO - BETA - Move the preload HTML as part of the engine driver. - preloadScene.load.html('inventory', inventoryTemplatePath+'ui-inventory.html'); - preloadScene.load.html('equipment', inventoryTemplatePath+'ui-equipment.html'); - preloadScene.load.html('inventoryItem', inventoryTemplatePath+'item.html'); - preloadScene.load.html('inventoryItemUse', inventoryTemplatePath+'usable.html'); - preloadScene.load.html('inventoryItemEquip', inventoryTemplatePath+'equip.html'); - preloadScene.load.html('inventoryGroup', inventoryTemplatePath+'group.html'); - preloadScene.load.html('inventoryTradeContainer', inventoryTemplatePath+'trade-container.html'); - preloadScene.load.html('inventoryTradePlayerContainer', inventoryTemplatePath+'trade-player-container.html'); - preloadScene.load.html('inventoryTradeRequirements', inventoryTemplatePath+'trade-requirements.html'); - preloadScene.load.html('inventoryTradeRewards', inventoryTemplatePath+'trade-rewards.html'); - preloadScene.load.html('inventoryTradeAction', inventoryTemplatePath+'trade-action.html'); - preloadScene.load.html('inventoryTradeActionRemove', inventoryTemplatePath+'trade-action-remove.html'); - preloadScene.load.html('inventoryTradeItem', inventoryTemplatePath+'trade-item.html'); - preloadScene.load.html('inventoryTradeItemQuantity', inventoryTemplatePath+'trade-item-quantity.html'); - preloadScene.load.html('inventoryTradeStart', inventoryTemplatePath+'trade-start.html'); - preloadScene.load.html('inventoryTradeAccept', inventoryTemplatePath+'trade-accept.html'); - } - onPreloadUiScene(preloadScene) { this.uiManager = new InventoryUi(preloadScene); this.uiManager.createUi(); - let inventoryPanel = preloadScene.getUiElement('inventory') - .getChildByProperty('id', InventoryConst.INVENTORY_ITEMS); - let equipmentPanel = preloadScene.getUiElement('equipment') - .getChildByProperty('id', InventoryConst.EQUIPMENT_ITEMS); - if(!inventoryPanel || !equipmentPanel){ - Logger.error(['Inventory/Equipment UI not found.', inventoryPanel, equipmentPanel]); + let inventoryPanel = preloadScene.getUiElement('inventory').getChildByProperty( + 'id', + InventoryConst.INVENTORY_ITEMS + ); + if(!inventoryPanel){ + Logger.error('Inventory UI not found.', inventoryPanel); + return false; + } + let equipmentPanel = preloadScene.getUiElement('equipment').getChildByProperty( + 'id', + InventoryConst.EQUIPMENT_ITEMS + ); + if(!equipmentPanel){ + Logger.error('Equipment UI not found.', equipmentPanel); return false; } let manager = preloadScene.gameManager.inventory.manager; - // first time load, then we listen the events to get the updates: - if(Object.keys(manager.groups).length){ + let inventoryGroups = sc.get(manager, 'groups', {}); + if(Object.keys(inventoryGroups).length){ preloadScene.gameManager.gameDom.getElement('#' + InventoryConst.EQUIPMENT_ITEMS).innerHTML = ''; - let orderedGroups = this.sortGroups(manager.groups); + let orderedGroups = this.sortGroups(inventoryGroups); for(let i of orderedGroups){ - let output = this.createGroupBox(manager.groups[i], preloadScene.gameManager, preloadScene); + let output = this.createGroupBox(inventoryGroups[i], preloadScene.gameManager, preloadScene); preloadScene.gameManager.gameDom.appendToElement('#' + InventoryConst.EQUIPMENT_ITEMS, output); } } - let itemsKeys = Object.keys(manager.items); + let itemsElements = sc.get(manager, 'items', {}); + let itemsKeys = Object.keys(itemsElements); if(0 < itemsKeys.length){ for(let i of itemsKeys){ - let item = manager.items[i]; + let item = itemsElements[i]; this.displayItem(item, preloadScene, equipmentPanel, inventoryPanel, i); } } @@ -111,7 +98,6 @@ class InventoryPlugin extends PluginInterface if(!roomEvents.gameManager.inventory){ this.createInventoryInstance(player, roomEvents); } - // listen to room messages: roomEvents.room.onMessage('*', (message) => { roomEvents.gameManager.inventory.processMessage(message); }); @@ -304,7 +290,6 @@ class InventoryPlugin extends PluginInterface details.style.display = 'block'; } }); - // show item trash: let buttonElement = inventoryPanel.querySelector('#item-trash-' + idx + ' img'); if(!buttonElement){ Logger.error(['Missing button.', buttonElement]); @@ -323,15 +308,19 @@ class InventoryPlugin extends PluginInterface }; preloadScene.gameManager.room.send('*', optionSend); }); - // use: if(this.isUsable(item)){ let useBtn = domMan.getElement('#item-use-'+idx); - useBtn.addEventListener('click', this.clickedBox.bind(this, idx, InventoryConst.ACTION_USE, preloadScene)); + useBtn.addEventListener( + 'click', + this.clickedBox.bind(this, idx, InventoryConst.ACTION_USE, preloadScene) + ); } - // equip / unequip: if(this.isEquipment(item)){ let equipBtn = domMan.getElement('#item-equip-'+idx); - equipBtn.addEventListener('click', this.clickedBox.bind(this, idx, InventoryConst.ACTION_EQUIP, preloadScene)); + equipBtn.addEventListener( + 'click', + this.clickedBox.bind(this, idx, InventoryConst.ACTION_EQUIP, preloadScene) + ); } } diff --git a/lib/inventory/client/templates-handler.js b/lib/inventory/client/templates-handler.js new file mode 100644 index 000000000..29a124f09 --- /dev/null +++ b/lib/inventory/client/templates-handler.js @@ -0,0 +1,35 @@ +/** + * + * Reldens - TemplatesHandler + * + */ + +class TemplatesHandler +{ + + static preloadTemplates(preloadScene) + { + // @TODO - BETA - Replace by loader replacing snake name file name by camel case for the template key. + let inventoryTemplatePath = 'assets/features/inventory/templates/'; + // @TODO - BETA - Move the preload HTML as part of the engine driver. + preloadScene.load.html('inventory', inventoryTemplatePath+'ui-inventory.html'); + preloadScene.load.html('equipment', inventoryTemplatePath+'ui-equipment.html'); + preloadScene.load.html('inventoryItem', inventoryTemplatePath+'item.html'); + preloadScene.load.html('inventoryItemUse', inventoryTemplatePath+'usable.html'); + preloadScene.load.html('inventoryItemEquip', inventoryTemplatePath+'equip.html'); + preloadScene.load.html('inventoryGroup', inventoryTemplatePath+'group.html'); + preloadScene.load.html('inventoryTradeContainer', inventoryTemplatePath+'trade-container.html'); + preloadScene.load.html('inventoryTradePlayerContainer', inventoryTemplatePath+'trade-player-container.html'); + preloadScene.load.html('inventoryTradeRequirements', inventoryTemplatePath+'trade-requirements.html'); + preloadScene.load.html('inventoryTradeRewards', inventoryTemplatePath+'trade-rewards.html'); + preloadScene.load.html('inventoryTradeAction', inventoryTemplatePath+'trade-action.html'); + preloadScene.load.html('inventoryTradeActionRemove', inventoryTemplatePath+'trade-action-remove.html'); + preloadScene.load.html('inventoryTradeItem', inventoryTemplatePath+'trade-item.html'); + preloadScene.load.html('inventoryTradeItemQuantity', inventoryTemplatePath+'trade-item-quantity.html'); + preloadScene.load.html('inventoryTradeStart', inventoryTemplatePath+'trade-start.html'); + preloadScene.load.html('inventoryTradeAccept', inventoryTemplatePath+'trade-accept.html'); + } + +} + +module.exports.TemplatesHandler = TemplatesHandler; diff --git a/lib/inventory/client/trade-message-handler.js b/lib/inventory/client/trade-message-handler.js index a53b31100..94a3107a3 100644 --- a/lib/inventory/client/trade-message-handler.js +++ b/lib/inventory/client/trade-message-handler.js @@ -56,24 +56,23 @@ class TradeMessageHandler { // @TODO - BETA - Make all these values configurable. let tradeUiKey = 'trade'+this.message.id; - let uiDoesNotExists = this.createTradeUi(tradeUiKey); - // this will create or reset the ui content: + this.createTradeUi(tradeUiKey); this.roomEvents.initUi({ id: tradeUiKey, - title: 'Trade request from:', + title: this.gameManager.config.getWithoutLogs( + 'client/trade/titles/tradeRequestFromLabel', + 'Trade request from:' + ), content: this.message.from, - options: {'1':{'label':'Accept','value':1},'2':{'label':'Decline','value':2}}, + options: this.gameManager.config.get('client/ui/options/acceptOrDecline'), overrideSendOptions: { act: InventoryConst.ACTIONS.TRADE_ACCEPTED, id: this.message.id } }); - if(uiDoesNotExists){ - this.gameDom.getElement('#opt-2-'+tradeUiKey)?.addEventListener('click', () => { - // this will send the close click action to the server: - this.gameDom.getElement('#box-close-'+tradeUiKey)?.click(); - }); - } + this.gameDom.getElement('#opt-2-'+tradeUiKey)?.addEventListener('click', () => { + this.gameDom.getElement('#box-close-'+tradeUiKey)?.click(); + }); } showTradeBox() @@ -109,8 +108,8 @@ class TradeMessageHandler createTradeUi(tradeUiKey) { - let uiDoesNotExists = !sc.hasOwn(this.roomEvents.tradeUi, tradeUiKey); - if (uiDoesNotExists) { + let tradeUi = sc.get(this.roomEvents.tradeUi, tradeUiKey); + if(!tradeUi){ this.roomEvents.tradeUi[tradeUiKey] = new UserInterface( this.gameManager, {id: tradeUiKey, type: 'trade'}, @@ -119,7 +118,7 @@ class TradeMessageHandler ); this.roomEvents.tradeUi[tradeUiKey].createUiElement(this.uiScene, 'trade'); } - return uiDoesNotExists; + return tradeUi; } updateItemsList(items, container, exchangeData) @@ -318,13 +317,28 @@ class TradeMessageHandler let functionLabels = ObjectsConst.TRADE_ACTIONS_FUNCTION_NAME; let templateParams = { tradeActionKey: this.message.id, - confirmLabel: this.gameManager.config.get('trade/titles/confirmLabel', functionLabels.CONFIRM), - disconfirmLabel: this.gameManager.config.get('trade/titles/disconfirmLabel', functionLabels.DISCONFIRM), - cancelLabel: this.gameManager.config.get('trade/titles/cancelLabel', functionLabels.CANCEL), + confirmLabel: this.gameManager.config.getWithoutLogs( + 'client/trade/titles/confirmLabel', + functionLabels.CONFIRM + ), + disconfirmLabel: this.gameManager.config.getWithoutLogs( + 'client/trade/titles/disconfirmLabel', + functionLabels.DISCONFIRM + ), + cancelLabel: this.gameManager.config.getWithoutLogs( + 'client/trade/titles/cancelLabel', + functionLabels.CANCEL + ), myItems: tradeItems, - myItemsTitle: this.gameManager.config.get('trade/titles/myItems', 'My Items:'), - pushedToTradeTitle: this.gameManager.config.get('trade/titles/pushedToTradeTitle', 'Sending:'), - gotFromTradeTitle: this.gameManager.config.get('trade/titles/gotFromTradeTitle', 'Receiving:'), + myItemsTitle: this.gameManager.config.getWithoutLogs('client/trade/titles/myItems', 'My Items:'), + pushedToTradeTitle: this.gameManager.config.getWithoutLogs( + 'client/trade/titles/pushedToTradeTitle', + 'Sending:' + ), + gotFromTradeTitle: this.gameManager.config.getWithoutLogs( + 'client/trade/titles/gotFromTradeTitle', + 'Receiving:' + ), playerConfirmedLabel: this.playerConfirmedLabel(), }; return this.gameManager.gameEngine.parseTemplate(messageTemplate, templateParams); @@ -332,9 +346,13 @@ class TradeMessageHandler playerConfirmedLabel() { - return this.message.playerConfirmed - ? this.message.with + this.gameManager.config.get('trade/titles/playerConfirmedLabel', ' CONFIRMED') - : ''; + if(!this.message.playerConfirmed){ + return ''; + } + return this.gameManager.config.getWithoutLogs( + 'client/trade/titles/playerConfirmedLabel', + '%playerName CONFIRMED' + ).replace('%playerName', this.message.with); } createTradeItemBox(item, exchangeDataItem) @@ -428,4 +446,4 @@ class TradeMessageHandler } -module.exports.TradeMessageHandler = TradeMessageHandler; \ No newline at end of file +module.exports.TradeMessageHandler = TradeMessageHandler; diff --git a/lib/inventory/client/trade-message-listener.js b/lib/inventory/client/trade-message-listener.js index f9b14796f..30a958514 100644 --- a/lib/inventory/client/trade-message-listener.js +++ b/lib/inventory/client/trade-message-listener.js @@ -14,12 +14,12 @@ class TradeMessageListener { let message = sc.get(props, 'message', false); if(!message){ - Logger.error('Missing message data.', props); + Logger.error('Missing message data on TradeMessageListener.', props); return false; } let roomEvents = sc.get(props, 'roomEvents', false); if(!roomEvents){ - Logger.error('Missing RoomEvents.', props); + Logger.error('Missing RoomEvents on TradeMessageListener.', props); return false; } let tradeMessageHandler = new TradeMessageHandler({roomEvents, message}); @@ -28,4 +28,4 @@ class TradeMessageListener } -module.exports.TradeMessageListener = TradeMessageListener; \ No newline at end of file +module.exports.TradeMessageListener = TradeMessageListener; diff --git a/lib/inventory/constants.js b/lib/inventory/constants.js index 33674ff99..7a00e7a0c 100644 --- a/lib/inventory/constants.js +++ b/lib/inventory/constants.js @@ -18,7 +18,7 @@ module.exports.InventoryConst = { EQUIPMENT_CLOSE: 'equipment-close', EQUIPMENT_OPEN: 'equipment-open', INVENTORY_PREF: pref, - // TODO - BETA - Move inside ACTIONS. + // @TODO - BETA - Move inside ACTIONS. ACTION_REMOVE: pref+'Rm', ACTION_USE: pref+'Use', ACTION_EQUIP: pref+'Eqi', diff --git a/lib/inventory/server/message-actions.js b/lib/inventory/server/message-actions.js index 1c242b02a..9aceabb7c 100644 --- a/lib/inventory/server/message-actions.js +++ b/lib/inventory/server/message-actions.js @@ -1,8 +1,6 @@ /** * - * Reldens - MessageActions - * - * Server side messages actions. + * Reldens - InventoryMessageActions * */ @@ -25,7 +23,7 @@ class InventoryMessageActions if(!sc.hasOwn(data, 'id') || !data.id){ return false; } - if(!sc.isFunction(data.id, 'indexOf') || 0 !== data.id.indexOf('trade')){ + if(!sc.isFunction(data.id['indexOf']) || 0 !== data.id.indexOf('trade')){ return false; } return this.closeTradeAction(client, data, room, playerSchema); @@ -149,8 +147,9 @@ class InventoryMessageActions { let subActionParam = sc.get(data, ObjectsConst.TRADE_ACTIONS.SUB_ACTION, false); let mappedSubAction = this.mapSubAction(subActionParam); - if(false === mappedSubAction || !sc.isFunction(PlayerProcessor, mappedSubAction)){ - Logger.critical('Missing mapped sub-action.', mappedSubAction); + let functionExists = sc.isFunction(PlayerProcessor[mappedSubAction]); + if(false === mappedSubAction || !functionExists){ + Logger.critical('Missing mapped sub-action: "'+(mappedSubAction || 'false')+'".', {functionExists}); return false; } let inventoryKey = this.isMyTrade(playerSchema) ? 'A' : 'B'; @@ -197,15 +196,22 @@ class InventoryMessageActions { // @TODO - BETA - Refactor when include false conditions in the shortcuts and include a new property "tradable". let fromInventoryItems = [ - // TODO - BETA - Refactor to make findItemsByPropertyValue always return an array. + // @TODO - BETA - Refactor to make findItemsByPropertyValue always return an array. ...(playerOwner.inventory.manager.findItemsByPropertyValue('equipped', false) || []), ...(playerOwner.inventory.manager.findItemsByPropertyValue('equipped', undefined) || []) ]; // the exchange key required for the items list is the opposite of the player inventory: let tradeInProgress = playerOwner.tradeInProgress; - let playerToExchangeKey = tradeInProgress.inventories['A'].owner.sessionId === playerTo.sessionId - ? 'A' - : 'B'; + if(!tradeInProgress){ + Logger.critical('Trade not longer in progress.'); + return false; + } + let ownerSessionId = tradeInProgress.inventories['A']?.owner?.sessionId; + if(!ownerSessionId){ + Logger.critical('Trade owner unavailable.'); + return false; + } + let playerToExchangeKey = ownerSessionId === playerTo.sessionId ? 'A' : 'B'; let traderItemsData = this.extractExchangeItemsDataFromInventory( playerToExchangeKey, tradeInProgress diff --git a/lib/inventory/server/plugin.js b/lib/inventory/server/plugin.js index 5c119cc49..744220f28 100644 --- a/lib/inventory/server/plugin.js +++ b/lib/inventory/server/plugin.js @@ -26,7 +26,7 @@ class InventoryPlugin extends PluginInterface this.events.on('reldens.serverReady', async (event) => { await ServerSubscriber.initializeInventory(event.serverManager.configManager, this.modelsManager); }); - this.events.on('reldens.createPlayerAfter', async (client, authResult, currentPlayer, room) => { + this.events.on('reldens.createPlayerStatsAfter', async (client, authResult, currentPlayer, room) => { await PlayerSubscriber.createPlayerInventory(client, currentPlayer, room, this.events, this.modelsManager); }); // when the client sent a message to any room it will be checked by all the global messages defined: diff --git a/lib/objects/client/animation-engine.js b/lib/objects/client/animation-engine.js index d352d4751..3cf080a26 100644 --- a/lib/objects/client/animation-engine.js +++ b/lib/objects/client/animation-engine.js @@ -17,6 +17,7 @@ const { Logger, sc } = require('@reldens/utils'); const { ObjectsConst } = require('../constants'); +const { GameConst } = require('../../game/constants'); class AnimationEngine { @@ -120,6 +121,7 @@ class AnimationEngine this.currentPreloader.objectsAnimations[this.key] = this.currentAnimation; let spriteX = this.positionFix ? this.animPos.x : this.x; let spriteY = this.positionFix ? this.animPos.y : this.y; + // This is where the animation is actually been created and stored. this.sceneSprite = currentScene.physics.add.sprite(spriteX, spriteY, this.asset_key); if(this.autoStart){ this.sceneSprite.anims.play(this.key, true); @@ -133,6 +135,7 @@ class AnimationEngine this.sceneSprite.setDepth(this.y + this.sceneSprite.body.height); currentScene.objectsAnimations[this.key] = this; this.gameManager.events.emitSync('reldens.createAnimationAfter', {animationEngine: this}); + return this.sceneSprite; } automaticDestroyOnComplete() @@ -172,7 +175,7 @@ class AnimationEngine this.sceneSprite.setInteractive({useHandCursor: true}).on('pointerdown', (e) => { // @NOTE: we avoid running the object interactions while any UI element is open, if we click on the UI the // elements in the background scene should not be executed. - if('CANVAS' !== e.downElement.nodeName){ + if(GameConst.CANVAS !== e.downElement.nodeName){ return false; } // @TODO - BETA - CHECK - TempId is a temporal fix for multiple objects case. diff --git a/lib/objects/client/plugin.js b/lib/objects/client/plugin.js index 8af86baf4..a86d789ec 100644 --- a/lib/objects/client/plugin.js +++ b/lib/objects/client/plugin.js @@ -79,7 +79,11 @@ class ObjectsPlugin extends PluginInterface if(sc.hasOwn(currentScene.anims.anims.entries, skillBullet)){ animKey = skillBullet; } - let bulletSprite = currentScene.physics.add.sprite(body.x, body.y, animKey); + let bulletSprite = currentScene?.physics?.add?.sprite(body.x, body.y, animKey); + if(!bulletSprite){ + Logger.warning('Could not create bullet sprite.', currentScene); + return false; + } bulletSprite.setDepth(300000); this.bullets[key] = bulletSprite; } @@ -265,27 +269,32 @@ class ObjectsPlugin extends PluginInterface await this.events.emit('reldens.createDynamicAnimationsBefore', this, sceneDynamic); for(let i of Object.keys(currentScene.objectsAnimationsData)){ let animProps = currentScene.objectsAnimationsData[i]; - if(!animProps.key){ - Logger.error(['Animation key not specified. Skipping registry:', animProps]); - continue; - } - animProps.frameRate = sceneDynamic.configuredFrameRate; - await this.events.emit('reldens.createDynamicAnimation_'+animProps.key, this, animProps); - // check for custom class: - let classDefinition = sceneDynamic.gameManager.config.getWithoutLogs( - 'client/customClasses/objects/'+animProps.key - ); - if(!classDefinition){ - // or set default: - classDefinition = AnimationEngine; - } - // create the animation object instance: - let animation = new classDefinition(sceneDynamic.gameManager, animProps, sceneDynamic); - // @NOTE: this will populate the objectsAnimations property in the current scene, see scene-dynamic. - animation.createAnimation(); + await this.createAnimationFromAnimData(animProps, sceneDynamic); } } + async createAnimationFromAnimData(animProps, sceneDynamic) + { + if(!animProps.key){ + Logger.error(['Animation key not specified. Skipping registry:', animProps]); + return false; + } + animProps.frameRate = sceneDynamic.configuredFrameRate; + await this.events.emit('reldens.createDynamicAnimation_'+animProps.key, this, animProps); + // check for custom class: + let classDefinition = sceneDynamic.gameManager.config.getWithoutLogs( + 'client/customClasses/objects/'+animProps.key + ); + if(!classDefinition){ + // or set default: + classDefinition = AnimationEngine; + } + // create the animationEngine object instance: + let animationEngine = new classDefinition(sceneDynamic.gameManager, animProps, sceneDynamic); + // @NOTE: this will populate the objectsAnimations property in the current scene, see scene-dynamic. + animationEngine.createAnimation(); + return animationEngine; + } } module.exports.ObjectsPlugin = ObjectsPlugin; diff --git a/lib/objects/constants.js b/lib/objects/constants.js index 1c8647776..7f3caaf78 100644 --- a/lib/objects/constants.js +++ b/lib/objects/constants.js @@ -15,6 +15,7 @@ module.exports.ObjectsConst = { TYPE_NPC: 'npc', TYPE_ENEMY: 'enemy', TYPE_TRADER: 'trader', + TYPE_DROP: 'drop', DYNAMIC_ANIMATION: 'dyn', DEFAULTS: { BASE_OBJECT: { diff --git a/lib/objects/server/manager.js b/lib/objects/server/manager.js index ebca6b129..08fddc070 100644 --- a/lib/objects/server/manager.js +++ b/lib/objects/server/manager.js @@ -5,6 +5,7 @@ */ const { Logger, sc } = require('@reldens/utils'); +const { DropObject } = require('./object/type/drop-object'); class ObjectsManager { @@ -52,60 +53,70 @@ class ObjectsManager } this.roomObjects = {}; // @NOTE: allow null index for multiple objects of the same type. - for(let objectData of this.roomObjectsData){ - // @NOTE: these classes are coming from the theme/plugins/objects/server.js file. - let objClass = this.config.get('server/customClasses/objects/'+objectData.object_class_key); - if(!objClass){ - Logger.error([ - 'ObjectManager class not found.', - '- Object ID:', objectData.id, - '- Custom class:', objectData.object_class_key - ]); - continue; + for (let objectData of this.roomObjectsData) { + await this.generateObjectFromObjectData(objectData); + } + } + + async generateObjectFromObjectData(objectData) + { + // @NOTE: these classes are coming from the theme/plugins/objects/server.js file. + let objClass = this.config.get('server/customClasses/objects/' + objectData.object_class_key); + if (!objClass) { + Logger.error([ + 'ObjectManager class not found.', + '- Object ID:', objectData.id, + '- Custom class:', objectData.object_class_key + ]); + return false; + } + let objProps = Object.assign( + { config: this.config, events: this.events, dataServer: this.dataServer }, + objectData + ); + this.prepareInitialStats(objProps); + try { + let objectInstance = new objClass(objProps); + this.attachToAnimations(objectInstance); + if (sc.hasOwn(objectInstance, 'multiple')) { + objectInstance.objProps = objProps; } - let objProps = Object.assign( - {config: this.config, events: this.events, dataServer: this.dataServer}, - objectData - ); - this.prepareInitialStats(objProps); - try { - let objectInstance = new objClass(objProps); - this.attachToAnimations(objectInstance); - if(sc.hasOwn(objectInstance, 'multiple')){ - objectInstance.objProps = objProps; - } - this.enrichWithMultipleAnimationsData(objectData, objectInstance); - this.attachToMessagesListeners(objectInstance, objectData); - this.prepareAssetsPreload(objectData); - await this.runAdditionalSetup(objectInstance, objectData); - // save object: - this.roomObjects[objectInstance.objectIndex] = objectInstance; - if(!this.roomObjectsByLayer[objectData.layer_name]){ - this.roomObjectsByLayer[objectData.layer_name] = {}; - } - this.roomObjectsByLayer[objectData.layer_name][objectData.id] = objectInstance; - } catch(err) { - Logger.error({'Error while generating object': err, 'Object Data': objectData}); + this.enrichWithMultipleAnimationsData(objectData, objectInstance); + this.attachToMessagesListeners(objectInstance, objectData); + this.prepareAssetsPreload(objectData); + await this.runAdditionalSetup(objectInstance, objectData); + this.events.emit('reldens.afterRunAdditionalSetup', { + objectInstance, + objectData, + objectsManager: this + }); + this.roomObjects[objectInstance.objectIndex] = objectInstance; + if (!this.roomObjectsByLayer[objectData.layer_name]) { + this.roomObjectsByLayer[objectData.layer_name] = {}; } + this.roomObjectsByLayer[objectData.layer_name][objectData.id] = objectInstance; + } catch (err) { + Logger.error({ 'Error while generating object': err, 'Object Data': objectData }); } } prepareInitialStats(objProps) { let stats = sc.get(objProps, 'objects_stats', []); - if(0 < stats.length){ - if(!objProps.initialStats){ - objProps.initialStats = {}; - } - for(let stat of stats){ - objProps.initialStats[stat.parent_stat.key] = stat.value; - } + if(0 === stats.length){ + return; + } + if(!objProps.initialStats){ + objProps.initialStats = {}; + } + for(let stat of stats){ + objProps.initialStats[stat.parent_stat.key] = stat.value; } } async runAdditionalSetup(objectInstance, objectData) { - if(!sc.isFunction(objectInstance, 'runAdditionalSetup')){ + if(!sc.isObjectFunction(objectInstance, 'runAdditionalSetup')){ return false; } objectInstance.runAdditionalSetup({objectsManager: this, objectData}); @@ -166,6 +177,28 @@ class ObjectsManager return false; } + removeObjectData(rewardObject) + { + this.removeFromPreloadAssetsArray(rewardObject.objects_assets); + delete this.roomObjects[rewardObject.objectIndex]; + delete this.objectsAnimationsData[rewardObject.objectIndex]; + } + + removeFromPreloadAssetsArray(objectAssets) + { + for (let objectAsset of objectAssets) { + this.preloadAssets = this.preloadAssets.filter(obj => obj.object_id !== objectAsset.object_id); + } + } + + hasRewardsDropped() { + for (let roomObject of Object.values(this.roomObjects)) { + if (roomObject instanceof DropObject) { + return true + } + } + return false; + } } module.exports.ObjectsManager = ObjectsManager; diff --git a/lib/objects/server/object/type/base-object.js b/lib/objects/server/object/type/base-object.js index 21e54fb32..abf29dca0 100644 --- a/lib/objects/server/object/type/base-object.js +++ b/lib/objects/server/object/type/base-object.js @@ -75,7 +75,7 @@ class BaseObject extends InteractionArea async runAdditionalSetup() { // @NOTE: implement what you need here. - Logger.alert('Method not implemented "runAdditionalSetup" on: '+this.key); + Logger.info('Method not implemented "runAdditionalSetup" on: '+this.key); } } diff --git a/lib/objects/server/object/type/drop-object.js b/lib/objects/server/object/type/drop-object.js new file mode 100644 index 000000000..a69ce44fb --- /dev/null +++ b/lib/objects/server/object/type/drop-object.js @@ -0,0 +1,26 @@ +/** + * + * Reldens - DropObject + * + */ + +const { AnimationObject } = require('./animation-object'); +const { RewardsConst } = require('../../../../rewards/constants'); + +class DropObject extends AnimationObject +{ + + constructor(props) + { + super(props); + this.type = RewardsConst.REWARDS_PICK_UP_ACT; + this.eventsPrefix = RewardsConst.DROP_EVENT_PREFIX; + this.listenMessages = true; + this.clientParams.type = RewardsConst.REWARDS_PICK_UP_ACT; + this.clientParams.isInteractive = true; + this.interactionArea = this.config.get('server/rewards/actions/interactionsDistance'); + } + +} + +module.exports.DropObject = DropObject; diff --git a/lib/objects/server/object/type/enemy-object.js b/lib/objects/server/object/type/enemy-object.js index 393d61a36..e77bfd675 100644 --- a/lib/objects/server/object/type/enemy-object.js +++ b/lib/objects/server/object/type/enemy-object.js @@ -2,20 +2,15 @@ * * Reldens - EnemyObject * - * This is an example object class, it extends from the NpcObject class and then define the specific parameters for the - * behavior and animations. - * The main point here is that this is just and example, and you could even create several NPCs and make them run any - * kind of actions at any time. - * */ const { NpcObject } = require('./npc-object'); const { Pve } = require('../../../../actions/server/pve'); +const { SkillsExtraData } = require('../../../../actions/server/skills/skills-extra-data'); const { ObjectsConst } = require('../../../constants'); const { GameConst } = require('../../../../game/constants'); -const { Logger, sc } = require('@reldens/utils'); const { SkillConst } = require('@reldens/skills'); -const { SkillsExtraData } = require('../../../../actions/server/skills/skills-extra-data'); +const { Logger, sc } = require('@reldens/utils'); class EnemyObject extends NpcObject { @@ -24,7 +19,6 @@ class EnemyObject extends NpcObject { super(props); this.hasState = true; - // @TODO - BETA - Remove from config and make enemy stats load dynamically (passed on props from storage). let configStats = sc.get(props, 'initialStats', this.config.get('server/enemies/initialStats')); this.initialStats = Object.assign({}, configStats); this.stats = Object.assign({}, configStats); @@ -96,7 +90,7 @@ class EnemyObject extends NpcObject } this.events.on('reldens.sceneRoomOnCreate', (room) => { room.roomWorld.on('postBroadphase', (event) => { - if(!this.battle.inBattleWithPlayer.length){ + if(0 === this.battle.inBattleWithPlayer.length){ this.waitForPlayersToEnterRespawnArea(event, room); } }); @@ -158,12 +152,8 @@ class EnemyObject extends NpcObject async executePhysicalSkill(target, executedSkill) { - let messageData = Object.assign({ - skillKey: executedSkill.key - }, - executedSkill.owner.getPosition() - ); - if(sc.isFunction(executedSkill.owner, 'getSkillExtraData')){ + 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)}); } @@ -180,16 +170,16 @@ class EnemyObject extends NpcObject 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 targetBody = target.physicalBody || target.objectBody; - if(targetBody){ - if(!targetBody.world){ - Logger.error('PhysicalBody world is null.', targetBody.id); - return false; - } - targetBody.world.shootBullet(from, to, executedSkill); + if(!targetBody){ + Logger.info('Target body is missing.'); + return false; + } + if(!targetBody.world){ + Logger.error('Target body world is missing. Body ID: '+ targetBody.id); + return false; } - return false; + targetBody.world.shootBullet(from, to, executedSkill); } getSkillExtraData(params) diff --git a/lib/objects/server/object/type/npc-object.js b/lib/objects/server/object/type/npc-object.js index 0bd86c7d9..bbd1854c4 100644 --- a/lib/objects/server/object/type/npc-object.js +++ b/lib/objects/server/object/type/npc-object.js @@ -27,7 +27,7 @@ class NpcObject extends AnimationObject // @NOTE: interaction area is how far the player can be from the object to validate the actions on click, this // area will be the valid-margin surrounding the object. this.interactionArea = this.config.get('server/objects/actions/interactionsDistance'); - this.closeInteractionOnOutOfReach = this.config.get( + this.closeInteractionOnOutOfReach = this.config.getWithoutLogs( 'server/objects/actions/closeInteractionOnOutOfReach', true ); diff --git a/lib/objects/server/object/type/trader-object.js b/lib/objects/server/object/type/trader-object.js index 397b7139e..53b8aec22 100644 --- a/lib/objects/server/object/type/trader-object.js +++ b/lib/objects/server/object/type/trader-object.js @@ -142,7 +142,7 @@ class TraderObject extends NpcObject } let subActionParam = sc.get(data, ObjectsConst.TRADE_ACTIONS.SUB_ACTION, false); let mappedSubAction = this.mapSubAction(subActionParam); - if(false !== mappedSubAction && sc.isFunction(Processor, mappedSubAction)){ + if(false !== mappedSubAction && sc.isFunction(Processor[mappedSubAction])){ return await this.processSubAction(mappedSubAction, tradeKey, data, playerSchema, inventoryKey, tradeAction, client); } return await this.initializeTransaction(tradeKey, data, playerSchema, inventoryKey, tradeAction, client); diff --git a/lib/prediction/client/prediction-world-creator.js b/lib/prediction/client/prediction-world-creator.js index fc0e5c5c2..d57a85d58 100644 --- a/lib/prediction/client/prediction-world-creator.js +++ b/lib/prediction/client/prediction-world-creator.js @@ -13,10 +13,6 @@ const { Logger, sc } = require('@reldens/utils'); class PredictionWorldCreator { - /** - * @param {SceneDynamic} scene - * @returns {Promise} - */ async createSceneWorld(scene) { if(!scene.experimentalClientPrediction){ diff --git a/lib/respawn/server/room-respawn.js b/lib/respawn/server/room-respawn.js index 4eebbfa75..f4c0d9beb 100644 --- a/lib/respawn/server/room-respawn.js +++ b/lib/respawn/server/room-respawn.js @@ -74,9 +74,18 @@ class RoomRespawn // add tile data to the object and create object instance: Object.assign(clonedObjProps, tileData); let objInstance = new objClass(clonedObjProps); - if(sc.isFunction(objInstance, 'runAdditionalRespawnSetup')){ + if(sc.isObjectFunction(objInstance, 'runAdditionalRespawnSetup')){ await objInstance.runAdditionalRespawnSetup(); } + this.events.emit('reldens.afterRunAdditionalRespawnSetup', { + objInstance, + clonedObjProps, + respawnArea, + multipleObj, + objClass, + objectIndex, + roomRespawn: this + }); let assetsArr = this.getObjectAssets(multipleObj); // @TODO - BETA - Objects could have multiple assets, need to implement and test the case. objInstance.clientParams.asset_key = assetsArr[0]; diff --git a/lib/rewards/client/plugin.js b/lib/rewards/client/plugin.js new file mode 100644 index 000000000..a08266011 --- /dev/null +++ b/lib/rewards/client/plugin.js @@ -0,0 +1,42 @@ +/** + * + * Reldens - Rewards Client Plugin + * + */ + +const { PluginInterface } = require('../../features/plugin-interface'); +const { RewardsMessageListener } = require('./rewards-message-listener'); +const { Logger, sc } = require('@reldens/utils'); + +class RewardsPlugin extends PluginInterface +{ + + setup(props) + { + if(!this.validateProps(props)){ + return false; + } + this.events.on('reldens.joinedRoom', (room, gameManager) => { + RewardsMessageListener.listenMessages(room, gameManager); + }); + } + + validateProps(props) + { + let isValid = true; + this.gameManager = sc.get(props, 'gameManager', false); + if(!this.gameManager){ + Logger.error('Game Manager undefined in RewardsPlugin.'); + isValid = false; + } + this.events = sc.get(props, 'events', false); + if(!this.events){ + Logger.error('EventsManager undefined in RewardsPlugin.'); + isValid = false; + } + return isValid; + } + +} + +module.exports.RewardsPlugin = RewardsPlugin; diff --git a/lib/rewards/client/rewards-message-listener.js b/lib/rewards/client/rewards-message-listener.js new file mode 100644 index 000000000..0d1c761ff --- /dev/null +++ b/lib/rewards/client/rewards-message-listener.js @@ -0,0 +1,135 @@ +/** + * + * Reldens - RewardsMessageListener + * + */ + +const { RewardsConst } = require('../constants'); +const { GameConst } = require('../../game/constants'); +const { Logger, sc } = require('@reldens/utils'); + +class RewardsMessageListener +{ + + static listenMessages(room, gameManager) + { + room.onMessage('*', (message) => { + let rewards = sc.get(message, RewardsConst.REWARDS, false); + if(rewards){ + this.loadRewards(rewards, gameManager); + } + if(RewardsConst.REMOVE_DROP === message.act){ + this.removeRewardById(message.id, gameManager); + } + }); + } + + static loadRewards(rewards, gameManager) + { + let currentScene = gameManager.getActiveScene(); + let gameConfig = gameManager.config; + let objectPlugin = gameManager.getFeature('objects'); + let loader = currentScene.load; + if(!this.validateParams({currentScene, gameConfig, objectPlugin, loader})){ + return false; + } + for(let [rewardId, reward] of Object.entries(rewards)){ + this.loadSpritesheet(reward, loader, gameConfig); + loader.once('complete', async () => { + await this.createRewardAnimation(objectPlugin, reward, rewardId, currentScene); + }); + } + loader.start(); + return true; + } + + static async createRewardAnimation(objectsPlugin, reward, rewardId, currentScene) + { + return await objectsPlugin.createAnimationFromAnimData({ + type: RewardsConst.REWARDS_PICK_UP_ACT, + enabled: true, + ui: true, + frameStart: reward[RewardsConst.REWARDS_PARAMS]['start'], + frameEnd: reward[RewardsConst.REWARDS_PARAMS]['end'], + repeat: reward[RewardsConst.REWARDS_PARAMS]['repeat'], + autoStart: true, + key: rewardId, + id: rewardId, + targetName: '', + layerName: rewardId, + isInteractive: true, + asset_key: reward[RewardsConst.REWARDS_ASSET_KEY], + x: reward.x, + y: reward.y, + yoyo: reward[RewardsConst.REWARDS_PARAMS]['yoyo'] + }, currentScene); + } + + static loadSpritesheet(reward, loader, gameConfig) + { + loader.spritesheet( + reward[RewardsConst.REWARDS_ASSET_KEY], + this.getSpritesheetPath(reward), + this.getRewardFrameConfig(reward[RewardsConst.REWARDS_PARAMS], gameConfig) + ); + } + + static getRewardFrameConfig(rewardParams, gameConfig) + { + return { + frameWidth: sc.get( + rewardParams, + 'frameWidth', + gameConfig.get('client/map/tileData/width', GameConst.GRAPHICS.FRAME_WIDTH) + ), + frameHeight: sc.get( + rewardParams, + 'frameHeight', + gameConfig.get('client/map/tileData/width', GameConst.GRAPHICS.FRAME_HEIGHT) + ) + }; + } + + static getSpritesheetPath(reward) + { + return RewardsConst.REWARDS_PATH + reward[RewardsConst.REWARDS_FILE] + '.png'; + } + + static removeRewardById(rewardId, gameManager) + { + if(!rewardId){ + return false; + } + let currentScene = gameManager.activeRoomEvents.getActiveScene(); + let rewardAnimation = sc.get(currentScene.objectsAnimations, rewardId, false); + if(!rewardAnimation){ + return false; + } + rewardAnimation.sceneSprite.destroy(); + delete currentScene.objectsAnimations[rewardId]; + } + + static validateParams(props) + { + let isValid = true; + if(!sc.get(props, 'currentScene', false)){ + Logger.error('Scene is undefined in Rewards Message Listener.'); + isValid = false; + } + if(!sc.get(props, 'gameConfig', false)){ + Logger.error('Game Config is undefined in Rewards Message Listener.'); + isValid = false; + } + if(!sc.get(props, 'objectPlugin', false)){ + Logger.error('Object Plugin is undefined in Rewards Message Listener.'); + isValid = false; + } + if(!sc.get(props, 'loader', false)){ + Logger.error('Loader is undefined in Rewards Message Listener.'); + isValid = false; + } + return isValid; + } +} + +module.exports.RewardsMessageListener = RewardsMessageListener; diff --git a/lib/rewards/constants.js b/lib/rewards/constants.js new file mode 100644 index 000000000..888f1b929 --- /dev/null +++ b/lib/rewards/constants.js @@ -0,0 +1,30 @@ +/** + * + * Reldens - RewardsConst + * + */ + +module.exports.RewardsConst = { + REWARDS: 'rw', + REWARDS_HAS_SPACE: 'hs', + REWARDS_TYPE: 'rt', + REWARDS_ASSET_KEY: 'rk', + REWARDS_FILE: 'rf', + REWARDS_PARAMS: 'rp', + REWARDS_PATH: 'assets/custom/sprites/', + REWARDS_PICK_UP_ACT: 'rpu', + DROP_EVENT_PREFIX: 'dep', + REMOVE_DROP: 'rd', + SPLIT_EXPERIENCE: { + ALL: 0, + PROPORTIONAL_BY_LEVEL: 1 + }, + SPLIT_MODIFIER: { + ALL: 0, + RANDOM: 1 + }, + SPLIT_ITEMS: { + DROP_KEEPS: 0, + RANDOM: 1 + } +}; diff --git a/lib/rewards/server/entities-config.js b/lib/rewards/server/entities-config.js new file mode 100644 index 000000000..57bead5ff --- /dev/null +++ b/lib/rewards/server/entities-config.js @@ -0,0 +1,22 @@ +/** + * + * Reldens - Rewards Entities Config + * + */ + +const { RewardsEntity } = require('./entities/rewards-entity'); +const { RewardsModifiersEntity } = require('./entities/rewards-modifiers-entity'); +const { ObjectsItemsRewardsAnimationsEntity } = require('./entities/objects-items-rewards-animations-entity'); + +let objectsConfig = { + parentItemLabel: 'Rewards', + icon: 'WatsonHealth3DSoftware' +}; + +let entitiesConfig = { + rewards: RewardsEntity.propertiesConfig(objectsConfig), + rewardsModifiers: RewardsModifiersEntity.propertiesConfig(objectsConfig), + objectsItemsRewardsAnimations: ObjectsItemsRewardsAnimationsEntity.propertiesConfig(objectsConfig) +}; + +module.exports.entitiesConfig = entitiesConfig; diff --git a/lib/rewards/server/entities-translations.js b/lib/rewards/server/entities-translations.js new file mode 100644 index 000000000..41bc8e51b --- /dev/null +++ b/lib/rewards/server/entities-translations.js @@ -0,0 +1,13 @@ +/** + * + * Reldens - Rewards Entities Translations + * + */ + +module.exports.entitiesTranslations = { + labels: { + rewards: 'Rewards', + rewardsModifiers: 'Rewards - Modifiers', + objectsItemsRewardsAnimations: 'Rewards - Animations' + } +}; diff --git a/lib/rewards/server/entities/objects-items-rewards-animations-entity.js b/lib/rewards/server/entities/objects-items-rewards-animations-entity.js new file mode 100644 index 000000000..b3c9b3125 --- /dev/null +++ b/lib/rewards/server/entities/objects-items-rewards-animations-entity.js @@ -0,0 +1,56 @@ +/** + * + * Reldens - ObjectsItemsRewardsAnimationsEntity + * + */ + +const { EntityProperties } = require('../../../game/server/entity-properties'); +const { sc } = require('@reldens/utils'); + +class ObjectsItemsRewardsAnimationsEntity extends EntityProperties +{ + + static propertiesConfig(extraProps) + { + let properties = { + id: { + isId: true + }, + reward_id: { + type: 'reference', + reference: 'rewards', + isRequired: true + }, + asset_type: { + isRequired: true + }, + asset_key: { + isRequired: true + }, + file: { + isRequired: true + }, + extra_params: {} + }; + + let listPropertiesKeys = Object.keys(properties); + let editPropertiesKeys = [...listPropertiesKeys]; + + listPropertiesKeys = sc.removeFromArray(listPropertiesKeys, [ + 'file', + 'extra_params' + ]); + editPropertiesKeys.splice(editPropertiesKeys.indexOf('id'), 1); + + return Object.assign({ + listProperties: listPropertiesKeys, + showProperties: Object.keys(properties), + filterProperties: listPropertiesKeys, + editProperties: editPropertiesKeys, + properties + }, extraProps); + } + +} + +module.exports.ObjectsItemsRewardsAnimationsEntity = ObjectsItemsRewardsAnimationsEntity; diff --git a/lib/rewards/server/entities/rewards-entity.js b/lib/rewards/server/entities/rewards-entity.js new file mode 100644 index 000000000..a75cb174d --- /dev/null +++ b/lib/rewards/server/entities/rewards-entity.js @@ -0,0 +1,66 @@ +/** + * + * Reldens - RewardsEntity + * + */ + +const { EntityProperties } = require('../../../game/server/entity-properties'); + +class RewardsEntity extends EntityProperties +{ + static propertiesConfig(extraProps) + { + let properties = { + id: {}, + object_id: { + type: 'reference', + reference: 'objects', + isRequired: true + }, + item_id: { + type: 'reference', + reference: 'items_item' + }, + modifier_id: { + type: 'reference', + reference: 'rewards_modifiers' + }, + experience: { + type: 'number' + }, + drop_rate: { + type: 'number', + isRequired: true + }, + drop_quantity: { + type: 'number', + isRequired: true + }, + is_unique: { + type: 'boolean' + }, + was_given: { + type: 'boolean' + }, + has_drop_body: { + type: 'boolean' + } + }; + + let listPropertiesKeys = Object.keys(properties); + let editPropertiesKeys = [...listPropertiesKeys]; + + editPropertiesKeys.splice(editPropertiesKeys.indexOf('id'), 1); + + return Object.assign({ + listProperties: listPropertiesKeys, + showProperties: Object.keys(properties), + filterProperties: listPropertiesKeys, + editProperties: editPropertiesKeys, + properties + }, extraProps); + } + +} + +module.exports.RewardsEntity = RewardsEntity; diff --git a/lib/rewards/server/entities/rewards-modifiers-entity.js b/lib/rewards/server/entities/rewards-modifiers-entity.js new file mode 100644 index 000000000..24522b4b2 --- /dev/null +++ b/lib/rewards/server/entities/rewards-modifiers-entity.js @@ -0,0 +1,70 @@ +/** + * + * Reldens - RewardsModifierEntity + * + */ + +const { EntityProperties } = require('../../../game/server/entity-properties'); +const { sc } = require('@reldens/utils'); + +class RewardsModifiersEntity extends EntityProperties +{ + static propertiesConfig(extraProps) + { + let properties = { + id: {}, + key: { + type: 'string', + isTitle: true, + isRequired: true + }, + property_key: { + type: 'string', + isRequired: true + }, + operation: { + availableValues: [ + { value: 1, label: 'Increment' }, + { value: 2, label: 'Decrease' }, + { value: 3, label: 'Divide' }, + { value: 4, label: 'Multiply' }, + { value: 5, label: 'Increment Percentage' }, + { value: 6, label: 'Decrease Percentage' }, + { value: 7, label: 'Set' }, + { value: 8, label: 'Method' }, + { value: 9, label: 'Set Number' } + ], + isRequired: true + }, + value: { + isRequired: true + }, + minValue: {}, + maxValue: {}, + minProperty: {}, + maxProperty: {} + }; + + let listPropertiesKeys = Object.keys(properties); + let editPropertiesKeys = [...listPropertiesKeys]; + + listPropertiesKeys = sc.removeFromArray(listPropertiesKeys, [ + 'minValue', + 'maxValue', + 'minProperty', + 'maxProperty' + ]); + editPropertiesKeys.splice(editPropertiesKeys.indexOf('id'), 1); + + return Object.assign({ + listProperties: listPropertiesKeys, + showProperties: Object.keys(properties), + filterProperties: listPropertiesKeys, + editProperties: editPropertiesKeys, + properties + }, extraProps); + } + +} + +module.exports.RewardsModifiersEntity = RewardsModifiersEntity; diff --git a/lib/rewards/server/message-actions.js b/lib/rewards/server/message-actions.js new file mode 100644 index 000000000..c51b5b5f1 --- /dev/null +++ b/lib/rewards/server/message-actions.js @@ -0,0 +1,85 @@ +/** + * + * Reldens - RewardMessageActions + * + */ + +const { RewardsConst } = require('../constants'); +const { ObjectsConst } = require('../../objects/constants'); +const { Logger, sc } = require('@reldens/utils'); + +class RewardMessageActions +{ + + constructor(targetDeterminer) + { + this.targetDeterminer = targetDeterminer; + } + + async executeMessageActions(client, message, room, playerSchema) + { + if(!sc.hasOwn(message, 'act') || !sc.hasOwn(message, 'type')){ + return false; + } + if(ObjectsConst.OBJECT_INTERACTION !== message.act || + RewardsConst.REWARDS_PICK_UP_ACT !== message.type){ + return false; + } + let rewardId = sc.get(message, 'id', false); + if(!rewardId){ + Logger.warning('A reward type message was received but without id'); + return false; + } + let rewardObject = sc.get(room.roomWorld.objectsManager.roomObjects, rewardId, false); + if(!rewardObject){ + Logger.error('Could not found reward ' + rewardId + ' on roomObjects'); + return false; + } + if(!rewardObject.isValidInteraction(playerSchema.state.x, playerSchema.state.y)){ + return false; + } + let eventData = { + rewardObject, + client, + room, + playerSchema, + continueEvent: true + } + await room.events.emit('reldens.beforeRemovingDroppedReward', eventData); + if(!eventData.continueEvent){ + return false; + } + let rewardTargets = RewardsConst.SPLIT_ITEMS.DROP_KEEPS === room.config.getWithoutLogs( + 'client/rewards/general/splitItems', + '' + ) ? {[playerSchema.player_id]: playerSchema} : this.targetDeterminer.forReward(playerSchema); + let playersKeys = Object.keys(rewardTargets); + let randomIndex = sc.randomInteger(0, (playersKeys.length -1)); + let randomTarget = rewardTargets[playersKeys[randomIndex]]; + await this.addItemToInventory(rewardObject.rewardModel, randomTarget); + RewardMessageActions.removeDrop(room, rewardObject); + room.broadcast('*', {act: RewardsConst.REMOVE_DROP, id: rewardObject.objectIndex}); + return true; + } + + static removeDrop(room, rewardObject) + { + // @TODO - BETA - Move the next 3 calls into a single method on the room class, like room.removeObject(). + room.roomWorld.removeBody(rewardObject.objectBody); + room.objectsManager.removeObjectData(rewardObject); + room.deleteObjectSceneData(rewardObject); + if(!room.objectsManager.hasRewardsDropped()){ + room.enableOnDispose(); + } + } + + async addItemToInventory(rewardModel, playerSchema) + { + let item = await playerSchema.skillsServer.dataServer.getEntity('item').loadById(rewardModel.itemId); + let rewardItem = playerSchema.inventory.manager.createItemInstance(item.key); + return await playerSchema.inventory.manager.addItem(rewardItem); + } + +} + +module.exports.RewardMessageActions = RewardMessageActions; diff --git a/lib/rewards/server/models/objection-js/objects-items-rewards-animations-model.js b/lib/rewards/server/models/objection-js/objects-items-rewards-animations-model.js new file mode 100644 index 000000000..c848572b1 --- /dev/null +++ b/lib/rewards/server/models/objection-js/objects-items-rewards-animations-model.js @@ -0,0 +1,34 @@ +/** + * + * Reldens - ObjectsItemsRewardsAnimationsModel + * + */ + +const { ObjectionJsRawModel } = require('@reldens/storage'); + +class ObjectsItemsRewardsAnimationsModel extends ObjectionJsRawModel +{ + + static get tableName() + { + return 'objects_items_rewards_animations'; + } + + static get relationMappings() + { + const { RewardsModel } = require('./rewards-model'); + return { + reward: { + relation: this.BelongsToOneRelation, + modelClass: RewardsModel, + join: { + from: this.tableName + '.reward_id', + to: RewardsModel.tableName + '.id' + } + } + }; + } + +} + +module.exports.ObjectsItemsRewardsAnimationsModel = ObjectsItemsRewardsAnimationsModel; diff --git a/lib/rewards/server/models/objection-js/registered-models-objection-js.js b/lib/rewards/server/models/objection-js/registered-models-objection-js.js new file mode 100644 index 000000000..76008d60d --- /dev/null +++ b/lib/rewards/server/models/objection-js/registered-models-objection-js.js @@ -0,0 +1,23 @@ +/** + * + * Reldens - Registered Entities + * + */ + +const { RewardsModel } = require('./rewards-model'); +const { RewardsModifiersModel } = require('./rewards-modifiers-model'); +const { ObjectsItemsRewardsAnimationsModel } = require('./objects-items-rewards-animations-model'); +const { entitiesTranslations } = require('../../entities-translations'); +const { entitiesConfig } = require('../../entities-config'); + +let rawRegisteredEntities = { + rewards: RewardsModel, + rewardsModifiers: RewardsModifiersModel, + objectsItemsRewardsAnimations: ObjectsItemsRewardsAnimationsModel, +}; + +module.exports.rawRegisteredEntities = rawRegisteredEntities; + +module.exports.entitiesConfig = entitiesConfig; + +module.exports.entitiesTranslations = entitiesTranslations; diff --git a/lib/rewards/server/models/objection-js/rewards-model.js b/lib/rewards/server/models/objection-js/rewards-model.js new file mode 100644 index 000000000..5422faf68 --- /dev/null +++ b/lib/rewards/server/models/objection-js/rewards-model.js @@ -0,0 +1,61 @@ +/** + * + * Reldens - RewardsModel + * + */ + +const { ObjectionJsRawModel } = require('@reldens/storage'); + +class RewardsModel extends ObjectionJsRawModel +{ + + static get tableName() + { + return 'rewards'; + } + + static get relationMappings() + { + const { ItemModel } = require('@reldens/items-system/lib/server/storage/models/objection-js/item-model'); + const { ObjectsModel } = require('../../../../objects/server/models/objection-js/objects-model'); + const { RewardsModifiersModel } = require('./rewards-modifiers-model'); + const { ObjectsItemsRewardsAnimationsModel } = require('./objects-items-rewards-animations-model'); + return { + objects: { + relation: this.HasOneRelation, + modelClass: ObjectsModel, + join: { + from: this.tableName + '.object_id', + to: ObjectsModel.tableName + '.id' + } + }, + items_item: { + relation: this.HasOneRelation, + modelClass: ItemModel, + join: { + from: this.tableName + '.item_id', + to: ItemModel.tableName + '.id' + } + }, + animations: { + relation: this.HasOneRelation, + modelClass: ObjectsItemsRewardsAnimationsModel, + join: { + from: this.tableName + '.id', + to: ObjectsItemsRewardsAnimationsModel.tableName + '.reward_id' + } + }, + modifier: { + relation: this.HasOneRelation, + modelClass: RewardsModifiersModel, + join: { + from: this.tableName + '.modifier_id', + to: RewardsModifiersModel.tableName + '.id' + } + } + }; + } + +} + +module.exports.RewardsModel = RewardsModel; diff --git a/lib/rewards/server/models/objection-js/rewards-modifiers-model.js b/lib/rewards/server/models/objection-js/rewards-modifiers-model.js new file mode 100644 index 000000000..590f580d3 --- /dev/null +++ b/lib/rewards/server/models/objection-js/rewards-modifiers-model.js @@ -0,0 +1,34 @@ +/** + * + * Reldens - RewardsModifiersModel + * + */ + +const { ObjectionJsRawModel } = require('@reldens/storage'); + +class RewardsModifiersModel extends ObjectionJsRawModel +{ + + static get tableName() + { + return 'rewards_modifiers'; + } + + static get relationMappings() + { + const { RewardsModel } = require('./rewards-model'); + return { + reward: { + relation: this.BelongsToOneRelation, + modelClass: RewardsModel, + join: { + from: this.tableName + '.id', + to: RewardsModel.tableName + '.modifier_id' + } + } + }; + } + +} + +module.exports.RewardsModifiersModel = RewardsModifiersModel; diff --git a/lib/rewards/server/objects-items-rewards-animations.js b/lib/rewards/server/objects-items-rewards-animations.js new file mode 100644 index 000000000..c1e116e5e --- /dev/null +++ b/lib/rewards/server/objects-items-rewards-animations.js @@ -0,0 +1,39 @@ +/** + * + * Reldens - ObjectsItemsRewardsAnimations + * + */ + +const { sc } = require('@reldens/utils'); + +class ObjectsItemsRewardsAnimations +{ + + constructor(props) + { + this.id = sc.get(props, 'id', 0); + this.rewardId = sc.get(props, 'rewardId', 0); + this.assetType = sc.get(props, 'assetType', ''); + this.assetKey = sc.get(props, 'assetKey', ''); + this.file = sc.get(props, 'file', ''); + this.extraParams = sc.get(props, 'extraParams', {}); + } + + static fromModel(model) + { + if(!model){ + return null; + } + return new ObjectsItemsRewardsAnimations({ + id: model.id, + rewardId: model.reward_id, + assetType: model.asset_type, + assetKey: model.asset_key, + file: model.file, + extraParams: sc.toJson(model.extra_params, {}), + }); + } + +} + +module.exports.ObjectsItemsRewardsAnimations = ObjectsItemsRewardsAnimations; diff --git a/lib/rewards/server/plugin.js b/lib/rewards/server/plugin.js new file mode 100644 index 000000000..0611f366a --- /dev/null +++ b/lib/rewards/server/plugin.js @@ -0,0 +1,52 @@ +/** + * + * Reldens - Rewards Server Plugin + * + */ + +const { ObjectSubscriber } = require('./subscribers/object-subscriber'); +const { RewardsSubscriber } = require('./subscribers/rewards-subscriber'); +const { RewardMessageActions } = require('./message-actions'); +const { RewardsDropsProcessor } = require('./rewards-drops-processor'); +const { TargetDeterminer } = require('./target-determiner'); +const { PluginInterface } = require('../../features/plugin-interface'); +const { ErrorManager, sc } = require('@reldens/utils'); + +class RewardsPlugin extends PluginInterface +{ + + setup(props) + { + this.events = sc.get(props, 'events', false); + if(!this.events){ + ErrorManager.error('EventsManager undefined in RewardsPlugin.'); + } + this.events.on('reldens.featuresManagerLoadFeaturesAfter', (event) => { + this.rewardsSubscriber = new RewardsSubscriber(event); + this.targetDeterminer = new TargetDeterminer(event?.featuresManager?.featuresList?.teams.package); + }); + this.events.on('reldens.afterRunAdditionalRespawnSetup', async (event) => { + await ObjectSubscriber.enrichWithRewards(event.objInstance); + }); + this.events.on('reldens.battleEnded', async (event) => { + await this.rewardsSubscriber.giveRewards( + event.playerSchema, + event.pve?.targetObject, + this.events + ); + }); + this.events.on('reldens.sceneRoomOnCreate', async (roomScene) => { + this.events.on('reldens.afterGiveRewards', async (rewardEventData) => { + await RewardsDropsProcessor.processRewardsDrops(roomScene, rewardEventData); + }); + }); + this.events.on('reldens.roomsMessageActionsGlobal', (roomMessageActions) => { + roomMessageActions.rewards = new RewardMessageActions( + this.targetDeterminer + ); + }); + } + +} + +module.exports.RewardsPlugin = RewardsPlugin; diff --git a/lib/rewards/server/reward.js b/lib/rewards/server/reward.js new file mode 100644 index 000000000..a2c0d395d --- /dev/null +++ b/lib/rewards/server/reward.js @@ -0,0 +1,147 @@ +/** + * + * Reldens - Reward + * + */ + +const { ObjectsItemsRewardsAnimations } = require('./objects-items-rewards-animations'); +const { ObjectsConst } = require('../../objects/constants'); +const { sc } = require('@reldens/utils'); + +class Reward +{ + + constructor(param) + { + this.id = sc.get(param, 'id', 0); + this.objectId = sc.get(param, 'objectId', 0); + this.itemId = sc.get(param, 'itemId', null); + this.modifierId = sc.get(param, 'modifierId', null); + this.experience = sc.get(param, 'experience', -1); + this.dropRate = sc.get(param, 'dropRate', -1); + this.dropQuantity = sc.get(param, 'dropQuantity', -1); + this.isUnique = 1 === sc.get(param,'isUnique', 0); + this.wasGiven = 1 === sc.get(param,'wasGiven', 0); + this.hasDropBody = 1 === sc.get(param,'hasDropBody', 0); + this.animationData = sc.get(param, 'animationData', null); + this.item = sc.get(param, 'item', null); + this.modifier = sc.get(param, 'modifier', null); + } + + isWinningReward() + { + if(0 === this.dropRate){ + return false; + } + return Math.floor(Math.random() * 100) <= this.dropRate; + } + + isValidReward() + { + if(0 === this.dropQuantity){ + return false; + } + if(this.isUnique && this.wasGiven){ + return false; + } + return !(!this.isItemType() && !this.isModifierType() && !this.hasExperienceSet()); + } + + isItemType() + { + return this.itemId; + } + + isModifierType() + { + return this.modifierId; + } + + hasExperienceSet() + { + return 0 < this.experience; + } + + isDroppable() + { + return this.hasDropBody && sc.get(this, 'animationData', false); + } + + static areValidRewards(rewards) + { + return 0 < (rewards?.length || 0); + } + + static getRewardsBag(rewards) + { + let itemRangeArray = []; + for(let reward of rewards){ + let itemRangeCount = itemRangeArray.length; + for(let i = 0; i < reward.dropRate; i++){ + itemRangeArray[itemRangeCount + i] = reward; + } + } + return itemRangeArray; + } + + static fromModel(rewardModel) + { + if(!rewardModel){ + return null; + } + return new Reward({ + 'id': rewardModel.id, + 'objectId': rewardModel.object_id, + 'itemId': rewardModel.item_id, + 'modifierId': rewardModel.modifier_id, + 'experience': rewardModel.experience, + 'dropRate': rewardModel.drop_rate, + 'dropQuantity': rewardModel.drop_quantity, + 'isUnique': rewardModel.is_unique, + 'wasGiven': rewardModel.was_given, + 'hasDropBody': rewardModel.has_drop_body, + 'animationData': ObjectsItemsRewardsAnimations.fromModel(rewardModel.animations), + 'item': rewardModel.items_item, + 'modifier': rewardModel.modifier + }); + } + + static createDropObjectData(reward, roomId) + { + return { + id: reward.randomRewardId + reward.tileIndex, + room_id: roomId, + layer_name: reward.randomRewardId, + tile_index: reward.tileIndex, + object_class_key: ObjectsConst.TYPE_DROP, + client_key: reward.randomRewardId + reward.tileIndex, + rewardModel: reward, + asset_key: reward.animationData.assetKey, + client_params: JSON.stringify({ + frameStart: reward.animationData.extraParams.start, + frameEnd: reward.animationData.extraParams.end, + repeat: sc.get(reward.animationData.extraParams, 'repeat', -1), + hideOnComplete: false, + autoStart: true, + asset_key: reward.animationData.assetKey, + yoyo: sc.get(reward.animationData.extraParams, 'yoyo', false) + }), + enabled: 1, + objects_assets: [{ + object_asset_id: null, + object_id: reward.randomRewardId, + asset_type: reward.animationData.assetType, + asset_key: reward.animationData.assetKey, + file_1: reward.animationData.file, + file_2: null, + extra_params: JSON.stringify({ + frameWidth: reward.animationData.extraParams.frameWidth, + frameHeight: reward.animationData.extraParams.frameHeight + }) + }] + }; + } + +} + +module.exports.Reward = Reward; diff --git a/lib/rewards/server/rewards-drops-mapper.js b/lib/rewards/server/rewards-drops-mapper.js new file mode 100644 index 000000000..b1783aceb --- /dev/null +++ b/lib/rewards/server/rewards-drops-mapper.js @@ -0,0 +1,33 @@ +/** + * + * Reldens - RewardsMapper + * + */ + +const { RewardsConst } = require('../constants'); + +class RewardsDropsMapper +{ + + static mapDropsData(rewards) + { + let messageData = { + [RewardsConst.REWARDS]: {} + }; + // @TODO - BETA - If possible move into a map method inside the reward model. + for(let reward of rewards){ + messageData[RewardsConst.REWARDS][reward.randomRewardId + reward.tileIndex] = { + [RewardsConst.REWARDS_TYPE]: reward.animationData.assetType, + [RewardsConst.REWARDS_ASSET_KEY]: reward.animationData.assetKey, + [RewardsConst.REWARDS_FILE]: reward.animationData.file, + [RewardsConst.REWARDS_PARAMS]: reward.animationData.extraParams, + x: reward.rewardPosition.x, + y: reward.rewardPosition.y + }; + } + return messageData; + } + +} + +module.exports.RewardsDropsMapper = RewardsDropsMapper; diff --git a/lib/rewards/server/rewards-drops-processor.js b/lib/rewards/server/rewards-drops-processor.js new file mode 100644 index 000000000..8404e77da --- /dev/null +++ b/lib/rewards/server/rewards-drops-processor.js @@ -0,0 +1,28 @@ +/** + * + * Reldens - RewardsDropsProcessor + * + */ + +const { WorldDropHandler } = require('./world-drop-handler'); +const { RewardsDropsMapper } = require('./rewards-drops-mapper'); + +class RewardsDropsProcessor +{ + + static async processRewardsDrops(roomScene, rewardEventData) + { + let rewards = await WorldDropHandler.createRewardItemObjectsOnRoom({ + roomScene, + rewardEventData + }); + if (!rewards) { + return; + } + // @TODO - BETA - Add new event before dispose and broadcast. + roomScene.disableOnDispose(); + roomScene.broadcast('*', RewardsDropsMapper.mapDropsData(rewards, roomScene)); + } +} + +module.exports.RewardsDropsProcessor = RewardsDropsProcessor; diff --git a/lib/rewards/server/rewards-mapper.js b/lib/rewards/server/rewards-mapper.js new file mode 100644 index 000000000..dfda673fb --- /dev/null +++ b/lib/rewards/server/rewards-mapper.js @@ -0,0 +1,26 @@ +/** + * + * Reldens - RewardsMapper + * + */ + +const { Reward } = require('./reward'); + +class RewardsMapper +{ + + static fromModels(rewardsModels) + { + if(0 === rewardsModels.length){ + return []; + } + let mapped = []; + for(let rewardModel of rewardsModels){ + mapped.push(Reward.fromModel(rewardModel)); + } + return mapped; + } + +} + +module.exports.RewardsMapper = RewardsMapper; diff --git a/lib/rewards/server/subscribers/object-subscriber.js b/lib/rewards/server/subscribers/object-subscriber.js new file mode 100644 index 000000000..14c3299ce --- /dev/null +++ b/lib/rewards/server/subscribers/object-subscriber.js @@ -0,0 +1,25 @@ +/** + * + * Reldens - ObjectSubscriber + * + */ + +const { RewardsMapper } = require('../rewards-mapper'); + +class ObjectSubscriber +{ + + static async enrichWithRewards(objectInstance) + { + objectInstance['rewards'] = RewardsMapper.fromModels( + await objectInstance.dataServer.getEntity('rewards').loadByWithRelations( + 'object_id', + objectInstance.id, + ['animations', 'items_item', 'modifier'] + ) + ); + } + +} + +module.exports.ObjectSubscriber = ObjectSubscriber; diff --git a/lib/rewards/server/subscribers/rewards-subscriber.js b/lib/rewards/server/subscribers/rewards-subscriber.js new file mode 100644 index 000000000..cc126a00e --- /dev/null +++ b/lib/rewards/server/subscribers/rewards-subscriber.js @@ -0,0 +1,212 @@ +/** + * + * Reldens - RewardsSubscriber + * + */ + +const { TargetDeterminer } = require('../target-determiner'); +const { Reward } = require('../reward'); +const { RewardsConst } = require('../../constants'); +const { Modifier, ModifierConst } = require('@reldens/modifiers'); +const { Logger, sc } = require('@reldens/utils'); + +class RewardsSubscriber +{ + + constructor(props) + { + // @TODO - BETA - Handle undefined cases. + this.teamsPlugin = props?.featuresManager?.featuresList?.teams.package; + this.rewardsConfig = props.featuresManager?.config?.getWithoutLogs('client/rewards/general', {}) ?? {}; + this.targetDeterminer = new TargetDeterminer(this.teamsPlugin); + } + + async giveRewards(playerSchema, targetObject, eventsManager) + { + let eventDrop = {playerSchema, targetObject, continueEvent: true}; + await eventsManager.emit('reldens.beforeGiveRewards', eventDrop); + if(!eventDrop.continueEvent){ + return; + } + let rewards = sc.get(targetObject, 'rewards', false); + let winningRewards = this.getWinningRewards(rewards); + if(0 === winningRewards.length){ + return; + } + let itemRewards = []; + for(let winningReward of winningRewards){ + let giveRewardErrors = await this.giveReward(winningReward, playerSchema, itemRewards); + if(0 < giveRewardErrors.length){ + Logger.error( + 'There was an error on the reward ID "'+winningReward.id+'".', + winningReward, + playerSchema, + giveRewardErrors + ); + } + } + // @TODO - BETA - Verify if we can update the unique reward if there was any errors. + await this.updateUniqueRewards(winningRewards, targetObject.dataServer); + if(0 === itemRewards.length){ + return; + } + await eventsManager.emit('reldens.afterGiveRewards', {itemRewards, playerSchema, targetObject, winningRewards}); + } + + async giveReward(reward, playerSchema, itemRewards) + { + // @TODO - BETA - Make optional give full records or partial ones. + // give by record will give a single reward model all the options to the same player (exp, item, modifier): + // let giveByRecord = this.config.get('rewards/general/giveByRecord'); + let rewardTargets = this.targetDeterminer.forReward(playerSchema); + let errors = []; + if(reward.isItemType()){ + let result = await this.applyItemReward(reward, rewardTargets, itemRewards); + if(!result.isSuccess){ + errors.push(result); + } + } + if(reward.isModifierType()){ + let result = await this.applyModifierReward(reward, rewardTargets); + if(!result.isSuccess){ + errors.push(result); + } + } + if(reward.hasExperienceSet()){ + let result = await this.applyExperienceReward(reward, rewardTargets); + if(!result.isSuccess){ + errors.push(result); + } + } + return errors; + } + + async applyItemReward(reward, rewardTargets, itemRewards) + { + let winningItem = reward.item; + if(!winningItem){ + return this.createRewardResult(false, 'The item with id ' + reward.itemId + ' was not found.'); + } + let playersKeys = Object.keys(rewardTargets); + let randomIndex = sc.randomInteger(0, (playersKeys.length -1)); + let randomTarget = rewardTargets[playersKeys[randomIndex]]; + if(reward.isDroppable()){ + // @TODO - BETA - Refactor, winningItem is not being used later. + itemRewards.push({winningItem, reward}); + return this.createRewardResult(true); + } + let addItemsResult = await this.addItemToPlayerInventory(randomTarget, winningItem, reward.dropQuantity); + if(!addItemsResult){ + return this.createRewardResult( + false, + 'Could not add item to Players Inventory - Reward ' + reward.id + ' - Item Id = ' + reward.itemId + ); + } + return this.createRewardResult(true); + } + + async applyModifierReward(reward, rewardTargets) + { + let rewardModifierModel = reward.modifier; + if(!rewardModifierModel){ + return this.createRewardResult(false, 'The modifier with id ' + reward.modifierId + ' was not found.'); + } + let playersKeys = Object.keys(rewardTargets); + let randomIndex = sc.randomInteger(0, playersKeys.length); + let randomTarget = rewardTargets[randomIndex]; + let configuredTargets = RewardsConst.SPLIT_MODIFIER.ALL === this.rewardsConfig['splitModifier'] + ? rewardTargets + : {[randomTarget.player_id]: randomTarget}; + for(let i of Object.keys(configuredTargets)){ + let modifier = new Modifier(rewardModifierModel); + modifier.apply(configuredTargets[i]); + if(ModifierConst.MOD_APPLIED !== modifier.state){ + return this.createRewardResult( + false, + 'Could not add modifier to Player - Reward ' + reward.id + ' - Modifier Id = ' + reward.modifierId + ); + } + } + return this.createRewardResult(true); + } + + async applyExperienceReward(reward, rewardTargets) + { + let playersKeys = Object.keys(rewardTargets); + if(RewardsConst.SPLIT_EXPERIENCE.ALL === this.rewardsConfig['splitExperience']){ + for(let i of playersKeys){ + let experiencePerPlayer = Number(reward.experience) / Number(playersKeys.length); + await rewardTargets[i].skillsServer.classPath.addExperience(experiencePerPlayer); + } + } + if(RewardsConst.SPLIT_EXPERIENCE.PROPORTIONAL_BY_LEVEL === this.rewardsConfig['splitExperience']){ + let levelsTotal = this.targetsLevelsTotal(playersKeys, rewardTargets); + for(let i of playersKeys){ + let playerLevelProportion = (rewardTargets[i].skillsServer.classPath.currentLevel * 100) / levelsTotal; + let experiencePerPlayer = (playerLevelProportion * Number(reward.experience)) / 100 ; + await rewardTargets[i].skillsServer.classPath.addExperience(experiencePerPlayer); + } + } + return this.createRewardResult(true); + } + + targetsLevelsTotal(playersKeys, rewardTargets) + { + let levelsTotal = 0; + for(let i of playersKeys){ + levelsTotal += Number(rewardTargets[i].skillsServer.classPath.currentLevel); + } + return levelsTotal; + } + + createRewardResult(isSuccess, message = '') + { + return { isSuccess, message }; + } + + async addItemToPlayerInventory(playerSchema, item, quantity) + { + let rewardItem = playerSchema.inventory.manager.createItemInstance(item.key, quantity); + if(!rewardItem){ + Logger.error(`Couldn't create item instance with key ${item.key}`); + return false; + } + return await playerSchema.inventory.manager.addItem(rewardItem); + } + + getWinningRewards(rewards, usesRewardBag = false) + { + let rewardAwarded = []; + if(!Reward.areValidRewards(rewards)){ + return rewardAwarded; + } + if(usesRewardBag){ + let rewardsBag = Reward.getRewardsBag(rewards); + let reward = rewardsBag[Math.floor(Math.random() * rewardsBag.length)]; + if(reward.isValidReward()){ + rewardAwarded.push(reward); + } + return rewardAwarded; + } + for(let reward of rewards){ + if(!reward.isWinningReward() || !reward.isValidReward()){ + continue; + } + rewardAwarded.push(reward); + } + return rewardAwarded; + } + + async updateUniqueRewards(rewards, dataServer) + { + let rewardsRepository = dataServer.getEntity('rewards'); + for(let reward of rewards){ + if(reward.isUnique && !reward.was_given){ + await rewardsRepository.updateById(reward.id, { was_given: 1 }); + } + } + } + +} + +module.exports.RewardsSubscriber = RewardsSubscriber; diff --git a/lib/rewards/server/target-determiner.js b/lib/rewards/server/target-determiner.js new file mode 100644 index 000000000..96d9bbdd9 --- /dev/null +++ b/lib/rewards/server/target-determiner.js @@ -0,0 +1,41 @@ +/** + * + * Reldens - TargetDeterminer + * + */ + +const { Logger, sc } = require('@reldens/utils'); + +class TargetDeterminer +{ + + constructor(teamsPlugin) + { + this.teamsPlugin = teamsPlugin; + } + + forReward(playerSchema) + { + let singleTarget = {[playerSchema.player_id]: playerSchema}; + if(!playerSchema.currentTeam){ + return singleTarget; + } + if(!this.teamsPlugin){ + Logger.error('TeamsPlugin undefined on RewardsSubscriber.'); + return singleTarget; + } + let playerTeam = sc.get(this.teamsPlugin.teams, playerSchema.currentTeam, false); + if(!playerTeam){ + Logger.error('Defined team ID on player not found.', { + currentTeam: playerSchema.currentTeam, + playerId: playerSchema.player_id, + teamsPlugin: this.teamsPlugin + }); + return singleTarget; + } + return 1 < Object.keys(playerTeam.players).length ? playerTeam.players : singleTarget; + } + +} + +module.exports.TargetDeterminer = TargetDeterminer; diff --git a/lib/rewards/server/world-drop-handler.js b/lib/rewards/server/world-drop-handler.js new file mode 100644 index 000000000..5467b2fd1 --- /dev/null +++ b/lib/rewards/server/world-drop-handler.js @@ -0,0 +1,147 @@ +/** + * + * Reldens - WorldDropHandler + * + */ + +const { Reward } = require('./reward'); +const { RewardMessageActions } = require('./message-actions'); +const { RewardsConst } = require('../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class WorldDropHandler +{ + + static async createRewardItemObjectsOnRoom(params) + { + let validParams = this.validateParams(params); + if(!validParams){ + return false; + } + let { targetObjectBody, itemRewards, roomScene } = validParams; + let closerWalkableNodes = this.generateWalkableNodesAround(targetObjectBody); + let rewardPosition = closerWalkableNodes.pop(); + let rewards = []; + // @TODO - BETA - Refactor, winningItem is not being used later. + for(let itemReward of itemRewards){ + for(let i = 0; i < itemReward.reward.dropQuantity; i++){ + if(1 < closerWalkableNodes.length){ + rewardPosition = closerWalkableNodes.pop(); + } + let tileIndex = targetObjectBody.world.tileIndexByRowAndColumn(rewardPosition.x, rewardPosition.y); + let newReward = sc.deepMergeProperties({ + rewardPosition, + tileIndex, + randomRewardId: 'reward-'+sc.randomChars(8) + }, itemReward.reward); + // @TODO - BETA - Check reward required properties. + let rewardObjectBody = await this.createRewardRoomObject(roomScene, targetObjectBody, newReward); + if(!rewardObjectBody){ + Logger.error('No object body for reward ' + newReward.id + ' could be created.'); + continue; + } + roomScene.addObjectStateSceneData(rewardObjectBody); + this.setRewardTimeDisappear(rewardObjectBody, roomScene); + newReward['objectBody'] = rewardObjectBody; + rewards.push(newReward); + } + } + return rewards; + } + + static generateWalkableNodesAround(targetObjectBody) + { + if(!targetObjectBody){ + Logger.critical('Undefined target object body.'); + return []; + } + let { currentCol, currentRow } = targetObjectBody; + let pathfinder = targetObjectBody.getPathFinder(); + let firstNode = pathfinder.grid.getNodeAt(currentCol, currentRow); + let firstWorldPosition = this.worldPositionForNode(firstNode, pathfinder.world.mapJson); + let nodes = [firstWorldPosition]; + for(let i = -1; i <= 1; i++){ + for(let j = -1; j <= 1; j++){ + let node = pathfinder.grid.getNodeAt(currentCol + i, currentRow + j); + if(node.walkable){ + nodes.push(this.worldPositionForNode(node, pathfinder.world.mapJson)); + } + } + } + return nodes; + } + + static worldPositionForNode(node, mapJson) + { + let tileW = mapJson.tilewidth, + tileH = mapJson.tileheight, + halfTileW = tileW / 2, + halfTileH = tileH / 2; + return { + x: node.x * tileW + halfTileW, + y: node.y * tileH + halfTileH + }; + } + + static getRewardPosition(closerWalkableNodes) + { + return closerWalkableNodes.splice(sc.randomInteger(0, closerWalkableNodes.length - 1), 1)[0]; + } + + static async createRewardRoomObject(roomScene, targetObjectBody, reward) + { + let dropObjectData = Reward.createDropObjectData(reward, roomScene.roomId); + await roomScene.objectsManager.generateObjectFromObjectData(dropObjectData); + return await roomScene.roomWorld.createRoomObjectBody( + { name: reward.randomRewardId }, + reward.tileIndex, + targetObjectBody.worldTileWidth, + targetObjectBody.worldTileHeight, + reward.rewardPosition.x, + reward.rewardPosition.y + ); + } + + static validateParams(params) + { + let rewardEventData = sc.get(params, 'rewardEventData', false); + if(!rewardEventData){ + Logger.critical('RewardEventData not found on WorldDropHandler.'); + return false; + } + let roomScene = sc.get(params, 'roomScene', false); + if(!roomScene){ + Logger.critical('RoomScene not found on WorldDropHandler.'); + return false; + } + let targetObjectBody = sc.get(rewardEventData.targetObject, 'objectBody', false); + if(!targetObjectBody){ + Logger.critical('Target object "objectBody" not found on WorldDropHandler.'); + return false; + } + let itemRewards = sc.get(rewardEventData, 'itemRewards', []); + if(0 === itemRewards.length){ + Logger.critical('Items rewards not found on WorldDropHandler.'); + return false; + } + return { + roomScene, + itemRewards, + targetObjectBody + }; + } + + static setRewardTimeDisappear(dropObject, roomScene) + { + setTimeout(() => { + if(!roomScene.objectsManager.getObjectData(dropObject.objectIndex)){ + return false; + } + RewardMessageActions.removeDrop(roomScene, dropObject); + roomScene.broadcast('*', {act: RewardsConst.REMOVE_DROP, id: dropObject.objectIndex}); + }, roomScene.config.get('server/rewards/actions/disappearTime')); + } + +} + +module.exports.WorldDropHandler = WorldDropHandler; diff --git a/lib/rooms/server/game.js b/lib/rooms/server/game.js index 5d3d82cd3..917bed7d8 100644 --- a/lib/rooms/server/game.js +++ b/lib/rooms/server/game.js @@ -27,7 +27,6 @@ class RoomGame extends RoomLogin async onJoin(client, options, authResult) { await this.events.emit('reldens.onJoinRoomGame', client, options, authResult, this); - // update last login: await this.loginManager.updateLastLogin(authResult); // we need to send the engine and all the general and client configurations from the storage: let storedClientConfig = {client: this.config.client}; @@ -36,13 +35,12 @@ class RoomGame extends RoomLogin act: GameConst.START_GAME, sessionId: client.sessionId, players: authResult.players, - // if multiplayer is disabled then we will use the first one as default: - player: authResult.players ? authResult.players[0] : false, + // @NOTE: if multiplayer is disabled then we will use the first one as default: + player: 0 < sc.length(authResult.players) ? authResult.players[0] : false, gameConfig: clientFullConfig, features: this.config.availableFeaturesList }; - await this.events.emit('reldens.beforeSuperInitialGameData', superInitialGameData, this); - // client start: + await this.events.emit('reldens.beforeSuperInitialGameData', superInitialGameData, this, client); client.send('*', superInitialGameData); } diff --git a/lib/rooms/server/login.js b/lib/rooms/server/login.js index 3ba661b22..105afc812 100644 --- a/lib/rooms/server/login.js +++ b/lib/rooms/server/login.js @@ -45,7 +45,7 @@ class RoomLogin extends Room } let loginResult = await this.loginManager.processUserRequest(options); if(sc.hasOwn(loginResult, 'error')){ - // @TODO - Beta - Improve login errors, use a send message with type and here just return false. + // @TODO - BETA - Improve login errors, use send message with type and here just return false. ErrorManager.error(loginResult.error); } if(sc.hasOwn(options, 'selectedPlayer')){ diff --git a/lib/rooms/server/scene.js b/lib/rooms/server/scene.js index 0d028ca26..dc08469ad 100644 --- a/lib/rooms/server/scene.js +++ b/lib/rooms/server/scene.js @@ -35,9 +35,7 @@ class RoomScene extends RoomLogin // related object instances will be removed when the room is disposed. let objectsManagerConfig = Object.assign({events: this.events}, options); this.objectsManager = new ObjectsManager(objectsManagerConfig); - // load the objects from the storage: await this.objectsManager.loadObjectsByRoomId(options.roomData.roomId); - // generate object instances: if(this.objectsManager.roomObjectsData){ await this.objectsManager.generateObjects(); } @@ -51,7 +49,6 @@ class RoomScene extends RoomLogin this.customData = options.roomData.customData || {}; WorldConfig.mapWorldConfigValues(this, this.config); this.allowSimultaneous = this.config.get('client/general/controls/allowSimultaneousKeys', true); - // create world: await this.createWorld(options.roomData, this.objectsManager); // the collisions manager has to be initialized after the world was created: this.collisionsManager = new CollisionsManager(this); @@ -93,7 +90,7 @@ class RoomScene extends RoomLogin } // we do not need to create a player entity since we only need the name for the chat: this.activePlayers[client.sessionId] = { - id: authResult.id, + userId: authResult.id, sessionId: client.sessionId, playerName: authResult.player.name, role_id: authResult.role_id, @@ -109,7 +106,7 @@ class RoomScene extends RoomLogin return false; } for(let i of this.playersKeysFromState()){ - let player = this.playerByIdFromState(i); + let player = this.playerBySessionIdFromState(i); if(player.username.toLowerCase() !== options.username.toLowerCase()){ continue; } @@ -125,28 +122,43 @@ class RoomScene extends RoomLogin return false; } - playerByIdFromState(id) + playerBySessionIdFromState(id) { + // @TODO - BETA - Refactor and extract Colyseus into a driver. return this.state.players.get(id); } + playerByPlayerIdFromState(playerId) + { + if(0 === this.playersCountInState()){ + return false; + } + for(let i of this.playersKeysFromState()){ + let tmp = this.playerBySessionIdFromState(i); + if(tmp.player_id === playerId){ + return tmp; + } + } + return false; + } + playersCountInState() { + // @TODO - BETA - Refactor and extract Colyseus into a driver. return this.state.players.size; } playersKeysFromState() { + // @TODO - BETA - Refactor and extract Colyseus into a driver. return Array.from(this.state.players.keys()); } async createPlayerOnScene(client, authResult) { await this.events.emit('reldens.createPlayerBefore', client, authResult, this); - // player creation: let currentPlayer = this.state.createPlayerSchema(authResult, client.sessionId); currentPlayer.persistData = async (params) => { - // persist data in player: await this.events.emit('reldens.playerPersistDataBefore', client, authResult, currentPlayer, params, this); await this.savePlayedTime(currentPlayer); await this.savePlayerState(currentPlayer.sessionId); @@ -157,7 +169,6 @@ class RoomScene extends RoomLogin currentPlayer.playStartTime = Date.now(); this.state.addPlayerToState(currentPlayer, client.sessionId); // @TODO - BETA - Create player body using a new pack in the world package. - // create body for server physics and assign the body to the player: currentPlayer.physicalBody = this.roomWorld.createPlayerBody({ id: client.sessionId, width: this.config.get('client/players/physicalBody/width'), @@ -218,33 +229,42 @@ class RoomScene extends RoomLogin async executeMovePlayerActions(playerSchema, messageData) { let bodyToMove = sc.get(playerSchema, 'physicalBody', false); - if(!bodyToMove || GameConst.STATUS.DEATH === playerSchema.inState){ + if(!bodyToMove){ + Logger.warning('Player ID "'+playerSchema.player_id+'" body not found.'); return false; } - // if player is moving: + if(GameConst.STATUS.DEATH === playerSchema.inState){ + return false; + } + if(GameConst.STOP === messageData.act){ + this.clearMovementIntervals(); + bodyToMove.stopMove(); + } let bodyCanMove = !bodyToMove.isChangingScene && !bodyToMove.isBlocked; + if(!bodyCanMove){ + return false; + } if(sc.hasOwn(messageData, 'dir') && bodyCanMove){ + // @TODO - BETA - Create a single class/point for setIntervals or timeOuts instances creation. this.movementInterval[messageData.dir] = setInterval( () => { bodyToMove.initMove(messageData.dir); }, bodyToMove.world.timeStep ); - } - // if player stopped: - if(GameConst.STOP === messageData.act){ - this.clearIntervals(); - bodyToMove.stopMove(); + return true; } let isPointer = GameConst.POINTER === messageData.act && this.config.get('client/players/tapMovement/enabled'); let hasColumnAndRow = sc.hasOwn(messageData, 'column') && sc.hasOwn(messageData, 'row'); if(isPointer && hasColumnAndRow){ + this.clearMovementIntervals(); + bodyToMove.stopMove(); messageData = this.pointsValidator.makeValidPoints(messageData); bodyToMove.moveToPoint(messageData); } } - clearIntervals() + clearMovementIntervals() { let intervalKeys = Object.keys(this.movementInterval); for(let i of intervalKeys){ @@ -272,7 +292,6 @@ class RoomScene extends RoomLogin async createWorld(roomData, objectsManager) { await this.events.emit('reldens.createWorld', roomData, objectsManager, this); - // create and assign world to room: this.roomWorld = this.createWorldInstance({ sceneName: this.roomName, roomId: roomData.roomId, @@ -284,9 +303,7 @@ class RoomScene extends RoomLogin worldConfig: this.worldConfig }); this.pointsValidator = new WorldPointsValidator(this.roomWorld.worldWidth, this.roomWorld.worldHeight); - // create world limits: this.roomWorld.createLimits(); - // add collisions: await this.roomWorld.createWorldContent(roomData); // start world movement from the config or with the default value: this.worldTimer = new WorldTimer({ @@ -309,16 +326,20 @@ class RoomScene extends RoomLogin return new P2world(data); } - async nextSceneInitialPosition(client, data) + async nextSceneInitialPosition(client, data, playerBody) { let nextRoom = await this.loginManager.roomsManager.loadRoomByName(data.next); if(!nextRoom){ - ErrorManager.error('Player room change error. Next room not found: ' + data.next); + Logger.error('Player room change error. Next room not found: ' + data.next); + playerBody.isChangingScene = false; + return; } - let currentPlayer = this.playerByIdFromState(client.sessionId); + let currentPlayer = this.playerBySessionIdFromState(client.sessionId); let newPosition = this.fetchNewPosition(nextRoom, data.prev); if(!newPosition){ - ErrorManager.error('Can not find next room: ' + nextRoom.roomName); + Logger.error('Can not find next room: '+nextRoom.roomName, {data, nextRoom}); + playerBody.isChangingScene = false; + return; } currentPlayer.state.scene = data.next; currentPlayer.state.room_id = nextRoom.roomId; @@ -328,6 +349,7 @@ class RoomScene extends RoomLogin let stateSaved = await this.savePlayerState(client.sessionId); if(!stateSaved){ Logger.error('Save state error: ' + client.sessionId); + playerBody.isChangingScene = false; return; } this.broadcastSceneChange(client, currentPlayer, data); @@ -353,6 +375,10 @@ class RoomScene extends RoomLogin broadcastSceneChange(client, currentPlayer, data) { // @NOTE: we need to broadcast the current player scene change to be removed or added in other players. + let showPlayedTimeConfig = this.config.getWithoutLogs( + 'client/players/playedTime/show', + GameConst.SHOW_PLAYER_TIME.NONE + ); this.broadcast('*', { act: GameConst.CHANGED_SCENE, id: client.sessionId, @@ -362,10 +388,10 @@ class RoomScene extends RoomLogin y: currentPlayer.state.y, dir: currentPlayer.state.dir, playerName: currentPlayer.playerName, - playedTime: currentPlayer.playedTime, + playedTime: GameConst.SHOW_PLAYER_TIME.ALL_PLAYERS === showPlayedTimeConfig ? currentPlayer.playedTime : -1, + avatarKey: currentPlayer.avatarKey }); - // remove body from server world: let bodyToRemove = currentPlayer.physicalBody; this.roomWorld.removeBody(bodyToRemove); // reconnect is to create the player in the new scene: @@ -374,18 +400,16 @@ class RoomScene extends RoomLogin async saveStateAndRemovePlayer(sessionId) { - // save the last state on the database: let savedPlayer = await this.savePlayerState(sessionId); - // first remove player body from current world: let playerSchema = this.getPlayerFromState(sessionId); - let isRemoveReady = true; - await this.events.emit('reldens.removeAllPlayerReferencesBefore', { + let stateObject = {isRemoveReady: true}; + await this.events.emit('reldens.saveStateAndRemovePlayerBefore', { room: this, savedPlayer, playerSchema, - isRemoveReady + stateObject }); - playerSchema && isRemoveReady + playerSchema && stateObject.isRemoveReady ? this.removeAllPlayerReferences(playerSchema, sessionId) : ErrorManager.error('Player not found, session ID: ' + sessionId); return savedPlayer; @@ -393,15 +417,11 @@ class RoomScene extends RoomLogin removeAllPlayerReferences(playerSchema, sessionId) { - // get body: let bodyToRemove = playerSchema.physicalBody; if(bodyToRemove){ - // remove body: this.roomWorld.removeBody(bodyToRemove); } - // remove the events: this.events.offByMasterKey(playerSchema.eventsPrefix + playerSchema.player_id); - // remove player: this.state.removePlayer(sessionId); } @@ -429,29 +449,31 @@ class RoomScene extends RoomLogin return playerSchema; } - async savePlayerStats(target, updateClient) + async savePlayerStats(playerSchema, client) { // @TODO - BETA - For now we are always updating all the stats but this can be improved to save only the ones // that changed. - // save the stats: - let updateReady = true; - this.events.emitSync('reldens.onSavePlayerStatsBefore', {room: this, target, updateClient, updateReady}); - if(!updateReady){ + let objectState = {updateReady: true}; + this.events.emitSync('reldens.onSavePlayerStatsBefore', {room: this, playerSchema, client, objectState}); + if(!objectState.updateReady){ return false; } - for(let i of Object.keys(target.stats)){ + for(let i of Object.keys(playerSchema.stats)){ let statId = this.config.client.players.initialStats[i].id; - // using a single update query we can easily update both value and base_value: let statPatch = { - value: target.stats[i], - base_value: target.statsBase[i] + value: playerSchema.stats[i], + base_value: playerSchema.statsBase[i] }; - await this.loginManager.usersManager.updatePlayerStatByIds(target.player_id, statId, statPatch); + await this.loginManager.usersManager.updatePlayerStatByIds(playerSchema.player_id, statId, statPatch); } - if(updateClient){ + if(client){ // @TODO - BETA - Convert all events in constants and consolidate them in a single file with descriptions. - await this.events.emit('reldens.savePlayerStatsUpdateClient', updateClient, target, this); - updateClient.send('*', {act: GameConst.PLAYER_STATS, stats: target.stats, statsBase: target.statsBase}); + await this.events.emit('reldens.savePlayerStatsUpdateClient', client, playerSchema, this); + client.send('*', { + act: GameConst.PLAYER_STATS, + stats: playerSchema.stats, + statsBase: playerSchema.statsBase + }); } return true; } @@ -462,7 +484,7 @@ class RoomScene extends RoomLogin let playedTime = Number(playerSchema.playedTime)+Number(Number(currentlyPlayedTime).toFixed(0)); playerSchema.playedTime = playedTime; let updateResult = await this.loginManager.usersManager.dataServer.getEntity('users').updateById( - playerSchema.id, + playerSchema.userId, {played_time: playedTime} ); if(!updateResult){ @@ -486,17 +508,19 @@ class RoomScene extends RoomLogin getPlayerFromState(playerIndex) { + // @TODO - BETA - Refactor and extract Colyseus into a driver. return this.state.players.get(playerIndex); } fetchActivePlayerById(playerId) { + // @TODO - BETA - Replace by secondary object activePlayersById using references to the same activePlayers. let activePlayersKeys = Object.keys(this.activePlayers); if(0 === activePlayersKeys.length){ return false; } for(let i of activePlayersKeys){ - if(playerId === this.activePlayers[i].id){ + if(playerId.toString() === this.activePlayers[i].playerData.id.toString()){ return this.activePlayers[i]; } } @@ -506,13 +530,15 @@ class RoomScene extends RoomLogin onDispose() { Logger.info('ON-DISPOSE Room: ' + this.roomName); - this.clearIntervals(); + this.clearMovementIntervals(); + clearInterval(this.roomWorld.worldTimer); // @TODO - BETA - Replace this by a master key related to the room ID and just remove all the events related // to this room. if(!this.roomWorld.respawnAreas){ return true; } // clean up the listeners! + // @TODO - BETA - Emit a new event for the room dispose and use listeners on each other core-plugin. for(let rI of Object.keys(this.roomWorld.respawnAreas)){ let instC = this.roomWorld.respawnAreas[rI].instancesCreated; for(let i of Object.keys(instC)){ @@ -527,6 +553,35 @@ class RoomScene extends RoomLogin } } + addObjectStateSceneData(object) + { + let sceneData = sc.toJson(this.state.sceneData); + sceneData.objectsAnimationsData[object.objectIndex] = this.roomData.objectsAnimationsData[object.objectIndex]; + for(let objectAsset of object.objects_assets){ + sceneData.preloadAssets.push(objectAsset); + } + this.state.sceneData = JSON.stringify(sceneData); + } + + deleteObjectSceneData(object) + { + let sceneData = sc.toJson(this.state.sceneData); + delete sceneData.objectsAnimationsData[object.objectIndex]; + for(let objectAsset of object.objects_assets){ + sceneData.preloadAssets = sceneData.preloadAssets.filter(obj => obj.object_id !== objectAsset.object_id); + } + this.state.sceneData = JSON.stringify(sceneData) + } + + disableOnDispose() + { + this.autoDispose = false; + } + + enableOnDispose() + { + this.autoDispose = true; + } } module.exports.RoomScene = RoomScene; diff --git a/lib/rooms/server/state.js b/lib/rooms/server/state.js index 4f519a9de..d72452aca 100644 --- a/lib/rooms/server/state.js +++ b/lib/rooms/server/state.js @@ -31,9 +31,9 @@ class State extends Schema } } - createPlayerSchema(playerData, id) + createPlayerSchema(playerData, sessionId) { - return new Player(playerData, id); + return new Player(playerData, sessionId); } addPlayerToState(playerSchema, id) @@ -44,13 +44,15 @@ class State extends Schema positionPlayer(id, data) { - if(!sc.hasOwn(this.players, id)){ + // @TODO - BETA - Refactor and extract Colyseus into a driver. + let player = this.players.get(id); + if(!player){ Logger.error('Player not found! ID: '+id); return false; } - this.players[id].state.mov = false; - this.players[id].state.x = data.x; - this.players[id].state.y = data.y; + player.state.mov = false; + player.state.x = data.x; + player.state.y = data.y; } removePlayer(id) diff --git a/lib/teams/client/clan-message-handler.js b/lib/teams/client/clan-message-handler.js new file mode 100644 index 000000000..492c0d054 --- /dev/null +++ b/lib/teams/client/clan-message-handler.js @@ -0,0 +1,410 @@ +/** + * + * Reldens - ClanMessageHandler + * + */ + +const { UserInterface } = require('../../game/client/user-interface'); +const { TeamsConst } = require('../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class ClanMessageHandler +{ + + constructor(props) + { + this.roomEvents = sc.get(props, 'roomEvents', false); + this.message = sc.get(props, 'message', false); + this.gameManager = this.roomEvents?.gameManager; + this.gameDom = this.gameManager?.gameDom; + this.uiScene = this.gameManager?.gameEngine?.uiScene; + } + + validate() + { + if(!this.roomEvents){ + Logger.info('Missing RoomEvents on ClanMessageHandler.'); + return false; + } + if(!this.message){ + Logger.info('Missing message on ClanMessageHandler.'); + return false; + } + if(!this.gameManager){ + Logger.info('Missing GameManager on ClanMessageHandler.'); + return false; + } + if(!this.uiScene){ + // @NOTE: the message could arrive before the uiScene gets ready. + // Logger.info('Missing UI Scene on ClanMessageHandler.'); + return false; + } + return this.gameManager.playerData?.id; + + } + + initializeClanUi() + { + this.uiScene.currentClan = false; + let clanUi = this.createClanUi(); + let title = this.gameManager.config.getWithoutLogs( + 'client/clan/labels/createClanTitle', + TeamsConst.LABELS.CLAN.CREATE_CLAN_TITLE + ); + let container = this.gameManager.gameDom.getElement('.clan-dialog-box .box-content'); + if(!container){ + Logger.error('Missing container: "#box-clan .box-content".'); + return false; + } + let uiBox = this.uiScene.elementsUi[TeamsConst.CLAN_KEY]; + if(!uiBox){ + Logger.error('Clan UI box not found.', {clanUi, container, uiBox}); + return false; + } + this.roomEvents.uiSetTitle(uiBox, {title}); + this.roomEvents.uiSetContent(uiBox, {content: this.createClanContent()}, this.uiScene); + this.activateCreateButton(); + this.addAndRemoveCaptureKeys(); + } + + showNewClan() + { + let clanUi = this.createClanUi(); + let title = this.gameManager.config.getWithoutLogs( + 'client/clan/labels/clanTitle', + TeamsConst.LABELS.CLAN.CLAN_TITLE + ).replace('%clanName', this.message.clanName) + .replace('%leaderName', this.gameManager.currentPlayerName()); + let container = this.gameManager.gameDom.getElement('.clan-dialog-box .box-content'); + if(!container){ + Logger.error('Missing container: ".clan-dialog-box .box-content".'); + return false; + } + let uiBox = this.uiScene.elementsUi[TeamsConst.CLAN_KEY]; + if(!uiBox){ + Logger.error('Clan UI box not found.', {clanUi, container, uiBox}); + return false; + } + this.roomEvents.uiSetTitle(uiBox, {title}); + this.roomEvents.uiSetContent(uiBox, {content: ''}, this.uiScene); + this.updateClanBox(container); + this.setClanFromMessage(); + } + + showClanRequest() + { + this.createClanUi(); + this.roomEvents.initUi({ + id: TeamsConst.CLAN_KEY, + title: this.gameManager.config.getWithoutLogs( + 'client/clan/labels/requestFromTitle', + TeamsConst.LABELS.CLAN.REQUEST_FROM + ), + content: this.message.from, + options: this.gameManager.config.get('client/ui/options/acceptOrDecline'), + overrideSendOptions: {act: TeamsConst.ACTIONS.CLAN_ACCEPTED, id: this.message.id} + }); + this.gameDom.getElement('#opt-2-clan')?.addEventListener('click', () => { + this.removeClanUi(); + }); + } + + showClanBox() + { + this.createClanUi(); + let title = this.gameManager.config.getWithoutLogs( + 'client/clan/labels/clanTitle', + TeamsConst.LABELS.CLAN.CLAN_TITLE + ) + .replace('%clanName', this.message.clanName) + .replace('%leaderName', this.message.leaderName); + let container = this.gameManager.gameDom.getElement('#box-clan .box-content'); + if(!container){ + Logger.error('Missing container: "#box-clan .box-content".'); + return false; + } + let uiBox = this.uiScene.elementsUi[TeamsConst.CLAN_KEY]; + this.roomEvents.uiSetTitle(uiBox, {title}); + this.roomEvents.uiSetContent(uiBox, {content: ''}, this.uiScene); + this.setClanFromMessage(); + this.updateClanBox(container); + } + + setClanFromMessage() + { + let players = sc.get(this.message, 'players', false); + let members = sc.get(this.message, 'members', false); + this.uiScene.currentClan = { + id: this.message.id, + name: this.message.clanName, + leader: this.message.leaderName, + ownerId: this.message.ownerId, + players, + members + }; + } + + removeClanUi() + { + let uiElement = this.gameManager.getUiElement(TeamsConst.CLAN_KEY); + if(!uiElement){ + Logger.error('Clan UI Element not found for remove.'); + return false; + } + uiElement.removeElement(); + delete this.uiScene.userInterfaces[TeamsConst.CLAN_KEY]; + delete this.uiScene.elementsUi[TeamsConst.CLAN_KEY]; + } + + createClanUi() + { + let clanUi = sc.get(this.uiScene.userInterfaces, TeamsConst.CLAN_KEY); + if(clanUi){ + return clanUi; + } + if(!this.uiScene.userInterfaces){ + this.uiScene.userInterfaces = {}; + } + this.uiScene.userInterfaces[TeamsConst.CLAN_KEY] = new UserInterface( + this.gameManager, + {id: TeamsConst.CLAN_KEY, type: TeamsConst.CLAN_KEY, defaultOpen: true, defaultClose: true}, + 'assets/features/teams/templates/ui-clan.html', + TeamsConst.CLAN_KEY + ); + this.uiScene.userInterfaces[TeamsConst.CLAN_KEY].createUiElement(this.uiScene, TeamsConst.CLAN_KEY); + return this.uiScene.userInterfaces[TeamsConst.CLAN_KEY]; + } + + createClanContent() + { + // @TODO - BETA - Move the template load from cache as part of the engine driver. + let templateContent = this.uiScene.cache.html.get('clanCreate'); + if(!templateContent){ + Logger.error('Missing template "clanCreate".'); + return ''; + } + let templateParams = { + playerId: this.gameManager.playerData.id.toString(), + createLabel: this.gameManager.config.getWithoutLogs( + 'client/clan/labels/createLabel', + TeamsConst.LABELS.CLAN.CREATE + ), + clanNamePlaceholder: this.gameManager.config.getWithoutLogs( + 'client/clan/labels/namePlaceholder', + TeamsConst.LABELS.CLAN.NAME_PLACEHOLDER + ) + }; + return this.gameManager.gameEngine.parseTemplate(templateContent, templateParams); + } + + activateCreateButton() + { + let createButton = this.gameManager.gameDom.getElement('.clan-dialog-box .submit-clan-create'); + if(!createButton){ + Logger.warning('Clan create button not found by ".clan-dialog-box .clan-create".'); + return false; + } + let nameInput = this.gameManager.gameDom.getElement('.clan-dialog-box .clan-name-input'); + if(!nameInput){ + Logger.warning('Clan create button not found by ".clan-dialog-box .clan-name-input".'); + return false; + } + createButton.addEventListener('click', () => { + if(0 === nameInput.value.length){ + return false; + } + this.gameManager.gameDom.updateContent( + '.clan-dialog-box .box-content', + this.uiScene.cache.html.get('uiLoading') + ); + this.gameManager.activeRoomEvents.room.send('*', { + act: TeamsConst.ACTIONS.CLAN_CREATE, + [TeamsConst.ACTIONS.CLAN_NAME]: nameInput.value + }); + }); + } + + updateClanBox(container) + { + let players = sc.get(this.message, 'players', []); + let connectedPlayersKeys = Object.keys(players); + let clanPlayers = 0 === connectedPlayersKeys.length ? this.gameManager.config.getWithoutLogs( + 'client/clan/labels/noneConnected', + TeamsConst.LABELS.CLAN.NONE_CONNECTED + ) : ''; + for(let i of connectedPlayersKeys){ + clanPlayers += this.createClanPlayerBox(players[i]); + } + let isPlayerOwner = this.gameManager.playerData.id.toString() === this.message.ownerId.toString(); + let members = sc.get(this.message, 'members', []); + let clanMembers = ''; + for(let i of Object.keys(members)){ + clanMembers += this.createClanMemberBox(members[i], isPlayerOwner); + } + container.innerHTML = this.createClanContainer(clanPlayers, clanMembers); + this.activateClanPlayersActions(players); + this.activateClanMembersActions(members); + this.activateClanLeaveButtonAction(); + } + + addAndRemoveCaptureKeys() + { + let dynamicScene = this.gameManager.getActiveScene(); + let keys = dynamicScene.availableControllersKeyCodes(); + let inputElement = this.gameManager.gameDom.getElement('.clan-name-input'); + dynamicScene.addAndRemoveCapture(keys, inputElement); + } + + createClanContainer(clanPlayers, clanMembers) + { + // @TODO - BETA - Move the template load from cache as part of the engine driver. + let templateContent = this.uiScene.cache.html.get('clanContainer'); + if(!templateContent){ + Logger.error('Missing template "clanContainer".'); + return ''; + } + let isPlayerOwner = this.gameManager.playerData.id.toString() === this.message.ownerId.toString(); + let leaveActionLabel = isPlayerOwner + ? this.gameManager.config.getWithoutLogs('client/clan/labels/disbandLabel', TeamsConst.LABELS.CLAN.DISBAND) + : this.gameManager.config.getWithoutLogs('client/clan/labels/leaveLabel', TeamsConst.LABELS.CLAN.LEAVE); + let templateParams = { + clanId: this.message.id, + playerId: this.gameManager.playerData.id.toString(), + leaveActionLabel: leaveActionLabel, + clanPlayersTitle: this.gameManager.config.getWithoutLogs( + 'client/clan/labels/clanPlayersTitle', + TeamsConst.LABELS.CLAN.PLAYERS_TITLE + ), + clanPlayers, + clanMembersTitle: this.gameManager.config.getWithoutLogs( + 'client/clan/labels/clanMembersTitle', + TeamsConst.LABELS.CLAN.MEMBERS_TITLE + ), + clanMembers + }; + return this.gameManager.gameEngine.parseTemplate(templateContent, templateParams); + } + + activateClanLeaveButtonAction() + { + let leaveButton = this.gameManager.gameDom.getElement('.leave-'+this.message.id); + leaveButton?.addEventListener('click', () => { + let sendData = { + act: TeamsConst.ACTIONS.CLAN_LEAVE, + id: this.message.id + }; + this.gameManager.activeRoomEvents.room.send('*', sendData); + }); + } + + createClanPlayerBox(playerData) + { + // @TODO - BETA - Move the template load from cache as part of the engine driver. + let templateContent = this.uiScene.cache.html.get('clanPlayerData'); + if(!templateContent){ + Logger.error('Missing template "clanPlayerData".'); + return ''; + } + return this.gameManager.gameEngine.parseTemplate(templateContent, { + playerId: playerData.player_id, + playerName: playerData.name, + playerProperties: this.createSharedPropertiesContent(playerData.sharedProperties), + }); + } + + createClanMemberBox(playerData, isPlayerOwner) + { + // @TODO - BETA - Move the template load from cache as part of the engine driver. + let templateContent = this.uiScene.cache.html.get('clanMemberData'); + if(!templateContent){ + Logger.error('Missing template "clanMemberData".'); + return ''; + } + let showPlayerRemove = playerData.id.toString() !== this.message.ownerId.toString(); + return this.gameManager.gameEngine.parseTemplate(templateContent, { + playerId: playerData.id.toString(), + playerName: playerData.name, + clanRemove: isPlayerOwner && showPlayerRemove ? this.createDismissPlayerButton(playerData) : '' + }); + } + + createDismissPlayerButton(playerData) + { + let templateContent = this.uiScene.cache.html.get('clanRemove'); + if(!templateContent){ + Logger.error('Missing template "clanRemove".'); + return ''; + } + return this.gameManager.gameEngine.parseTemplate(templateContent, {playerId: playerData.id.toString()}); + } + + createSharedPropertiesContent(playerSharedProperties) + { + let templateContent = this.uiScene.cache.html.get('teamsSharedProperty'); + if(!templateContent){ + Logger.error('Missing template "teamsSharedProperty".'); + return ''; + } + let sharedPropertiesContent = ''; + // @TODO - BETA - Move the template load from cache as part of the engine driver. + for(let i of Object.keys(playerSharedProperties)){ + templateContent = this.uiScene.cache.html.get('teamsSharedProperty'); + let propertyData = playerSharedProperties[i]; + let propertyMaxValue = sc.get(propertyData, 'max', ''); + if('' !== propertyMaxValue){ + propertyMaxValue = this.gameManager.config.getWithoutLogs( + 'client/clan/labels/propertyMaxValue', + TeamsConst.LABELS.CLAN.PROPERTY_MAX_VALUE + ).replace('%propertyMaxValue', propertyMaxValue); + } + sharedPropertiesContent += this.gameManager.gameEngine.parseTemplate(templateContent, { + key: i, + label: propertyData.label, + value: propertyData.value, + max: propertyMaxValue + }); + } + return sharedPropertiesContent; + } + + activateClanPlayersActions(playersData) + { + for(let i of Object.keys(playersData)){ + let playerData = playersData[i]; + let selectorPlayerName = '.clan-player-'+i+' .player-name'; + let selectorPlayerProperties = '.clan-player-'+i+' .properties-list-container'; + let playerNameElement = this.gameDom.getElement(selectorPlayerName); + if(!playerNameElement){ + Logger.notice('Player name element not found.', selectorPlayerName); + } + playerNameElement?.addEventListener('click', () => { + this.gameManager.getCurrentPlayer().setTargetPlayerById(playerData.sessionId); + }); + let playerPropertiesElement = this.gameDom.getElement(selectorPlayerProperties); + if(!playerNameElement){ + Logger.notice('Player properties element not found.', selectorPlayerProperties); + } + playerPropertiesElement?.addEventListener('click', () => { + this.gameManager.getCurrentPlayer().setTargetPlayerById(playerData.sessionId); + }); + } + } + + activateClanMembersActions(membersData) + { + for(let i of Object.keys(membersData)){ + let memberData = membersData[i]; + let selectorPlayerRemove = '.clan-member-' + memberData.id + ' .clan-remove-button'; + this.gameDom.getElement(selectorPlayerRemove)?.addEventListener('click', () => { + this.gameManager.activeRoomEvents.room.send('*', { + act: TeamsConst.ACTIONS.CLAN_REMOVE, + id: this.message.id, + remove: memberData.id + }); + }); + } + } + +} + +module.exports.ClanMessageHandler = ClanMessageHandler; diff --git a/lib/teams/client/clan-message-listener.js b/lib/teams/client/clan-message-listener.js new file mode 100644 index 000000000..7422adf82 --- /dev/null +++ b/lib/teams/client/clan-message-listener.js @@ -0,0 +1,72 @@ +/** + * + * Reldens - ClanMessageListener + * + */ + +const { ClanMessageHandler } = require('./clan-message-handler'); +const { TeamsConst } = require('../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class ClanMessageListener +{ + + async executeClientMessageActions(props) + { + let message = sc.get(props, 'message', false); + if(!message){ + Logger.error('Missing message data on ClanMessageListener.', props); + return false; + } + let roomEvents = sc.get(props, 'roomEvents', false); + if(!roomEvents){ + Logger.error('Missing RoomEvents on ClanMessageListener.', props); + return false; + } + let clanMessageHandler = new ClanMessageHandler({roomEvents, message}); + if(!clanMessageHandler.validate()){ + if(this.isClanMessage(message)){ + if(!roomEvents.clanMessagesQueue){ + roomEvents.clanMessagesQueue = []; + } + roomEvents.clanMessagesQueue.push(message); + } + return false; + } + if(!this.isClanMessage(message)){ + return false; + } + return this.handleClanMessage(message, clanMessageHandler); + } + + handleClanMessage(message, clanMessageHandler) + { + if(TeamsConst.ACTIONS.CLAN_INITIALIZE === message.act){ + return clanMessageHandler.initializeClanUi(); + } + if(TeamsConst.ACTIONS.CLAN_CREATE === message.act){ + if(TeamsConst.VALIDATION.SUCCESS === message.result){ + return clanMessageHandler.showNewClan(); + } + return clanMessageHandler.initializeClanUi(); + } + if(TeamsConst.ACTIONS.CLAN_INVITE === message.act){ + return clanMessageHandler.showClanRequest(); + } + if(TeamsConst.ACTIONS.CLAN_UPDATE === message.act){ + return clanMessageHandler.showClanBox(); + } + if(TeamsConst.ACTIONS.CLAN_LEFT === message.act){ + return clanMessageHandler.removeClanUi(); + } + return true; + } + + isClanMessage(message) + { + return 0 === message.act.indexOf(TeamsConst.CLAN_PREF); + } + +} + +module.exports.ClanMessageListener = ClanMessageListener; diff --git a/lib/teams/client/messages-processor.js b/lib/teams/client/messages-processor.js new file mode 100644 index 000000000..b0575adfe --- /dev/null +++ b/lib/teams/client/messages-processor.js @@ -0,0 +1,44 @@ +/** + * + * Reldens - MessagesProcessor + * + */ + +const { ClanMessageHandler } = require('./clan-message-handler'); +const { TeamMessageHandler } = require('./team-message-handler'); +const { sc } = require('@reldens/utils'); + +class MessagesProcessor +{ + + static processClanMessagesQueue(roomEvents, clanMessageListener) + { + if(!sc.isArray(roomEvents.clanMessagesQueue)){ + return; + } + if(0 === roomEvents.clanMessagesQueue.length){ + return; + } + for(let message of roomEvents.clanMessagesQueue){ + clanMessageListener.handleClanMessage(message, new ClanMessageHandler({roomEvents, message})); + } + roomEvents.clanMessagesQueue = []; + } + + static processTeamMessagesQueue(roomEvents, teamMessageListener) + { + if(!sc.isArray(roomEvents.teamMessagesQueue)){ + return; + } + if(0 === roomEvents.teamMessagesQueue.length){ + return; + } + for(let message of roomEvents.teamMessagesQueue){ + teamMessageListener.handleTeamMessage(message, new TeamMessageHandler({roomEvents, message})); + } + roomEvents.teamMessagesQueue = []; + } + +} + +module.exports.MessageProcessor = MessagesProcessor; diff --git a/lib/teams/client/plugin.js b/lib/teams/client/plugin.js new file mode 100644 index 000000000..62aa2ada3 --- /dev/null +++ b/lib/teams/client/plugin.js @@ -0,0 +1,74 @@ +/** + * + * Reldens - Teams Client Plugin + * + */ + +const { PluginInterface } = require('../../features/plugin-interface'); +const { TargetBoxEnricher } = require('./target-box-enricher'); +const { TeamMessageListener } = require('./team-message-listener'); +const { ClanMessageListener } = require('./clan-message-listener'); +const { MessageProcessor } = require('./messages-processor'); +const { TemplatesHandler } = require('./templates-handler'); +const { TeamsConst } = require('../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class TeamsPlugin extends PluginInterface +{ + + setup(props) + { + this.gameManager = sc.get(props, 'gameManager', false); + if(!this.gameManager){ + Logger.error('Game Manager undefined in TeamsPlugin.'); + } + this.events = sc.get(props, 'events', false); + if(!this.events){ + Logger.error('EventsManager undefined in TeamsPlugin.'); + } + this.teamMessageListener = new TeamMessageListener(); + this.clanMessageListener = new ClanMessageListener(); + this.events.on('reldens.createEngineSceneDone', (uiScene) => { + let roomEvents = uiScene?.gameManager?.activeRoomEvents || this.gameManager?.activeRoomEvents; + if(!roomEvents){ + Logger.critical('RoomEvents undefined for process Team messages queue on TeamsPlugin.'); + return false; + } + MessageProcessor.processClanMessagesQueue(roomEvents, this.clanMessageListener); + MessageProcessor.processTeamMessagesQueue(roomEvents, this.teamMessageListener); + }); + this.events.on('reldens.preloadUiScene', (preloadScene) => { + TemplatesHandler.preloadTemplates(preloadScene); + }); + this.events.on('reldens.gameEngineShowTarget', (gameEngine, target, previousTarget, targetName) => { + TargetBoxEnricher.appendClanInviteButton(this.gameManager, target, previousTarget, targetName); + TargetBoxEnricher.appendTeamInviteButton(this.gameManager, target, previousTarget, targetName); + }); + // @TODO - BETA - Standardize, listeners on config or added by events like: + // this.events.on('reldens.activateRoom', (room) => { + // room.onMessage('*', (message) => { + // this.messagesHandler.processOrQueueMessage(message); + // }); + // }); + this.gameManager.config.client.message.listeners[TeamsConst.KEY] = this.teamMessageListener; + this.gameManager.config.client.message.listeners[TeamsConst.CLAN_KEY] = this.clanMessageListener; + } + + fetchTeamPlayerBySessionId(sessionId) + { + let currentTeam = this.gameManager.gameEngine.uiScene.currentTeam; + if(!currentTeam){ + return false; + } + for(let i of Object.keys(currentTeam)){ + let teamPlayer = currentTeam[i]; + if(teamPlayer.sessionId === sessionId){ + return teamPlayer; + } + } + return false; + } + +} + +module.exports.TeamsPlugin = TeamsPlugin; diff --git a/lib/teams/client/target-box-enricher.js b/lib/teams/client/target-box-enricher.js new file mode 100644 index 000000000..4f720d49f --- /dev/null +++ b/lib/teams/client/target-box-enricher.js @@ -0,0 +1,111 @@ +/** + * + * Reldens - TargetBoxEnricher + * + */ + +const { TeamsConst } = require('../constants'); +const { GameConst } = require('../../game/constants'); +const { Logger, sc } = require('@reldens/utils'); + +class TargetBoxEnricher +{ + + static appendClanInviteButton(gameManager, target, previousTarget, targetName) + { + let currentClan = gameManager?.gameEngine?.uiScene?.currentClan; + if(!currentClan){ + // current player has none clan: + return false; + } + if(!currentClan.ownerId){ + Logger.error('Current clan missing owner.', currentClan); + return false; + } + if(this.playerBySessionId(currentClan, target.id)){ + // target player is already on the clan: + return false; + } + let currentPlayer = gameManager.getCurrentPlayer(); // @TODO - Reviewed: OK + if(!this.targetIsValidPlayer(target, currentPlayer)){ + // target is not the current player + return false; + } + let isOpenInvites = gameManager.config.getWithoutLogs('client/clan/general/openInvites', false); + if(gameManager.playerData.id.toString() !== currentClan.ownerId.toString() && !isOpenInvites){ + // only clan owner can invite: + return false; + } + return this.appendInviteButton('clan', target, gameManager, targetName); + } + + static appendTeamInviteButton(gameManager, target, previousTarget, targetName) + { + if(!this.targetIsValidPlayer(target, gameManager.getCurrentPlayer())){ // @TODO - Reviewed: OK + return false; + } + if(gameManager.getFeature('teams').fetchTeamPlayerBySessionId(target.id)){ + return false; + } + return this.appendInviteButton('team', target, gameManager, targetName); + } + + static appendInviteButton(type, target, gameManager, targetName) + { + let uiScene = gameManager.gameEngine.uiScene; + let uiTarget = sc.get(uiScene, 'uiTarget', false); + if(false === uiTarget){ + Logger.critical('Missing "uiTarget" on uiScene.'); + return false; + } + let teamPlayerActionsTemplate = uiScene.cache.html.get(type+'PlayerInvite'); + if(!teamPlayerActionsTemplate){ + Logger.critical('Template "'+type+'PlayerInvite" not found.'); + return false; + } + gameManager.gameDom.appendToElement( + '#target-container', + gameManager.gameEngine.parseTemplate( + teamPlayerActionsTemplate, + { + // @TODO - BETA - Create translations table with a loader and processor. + playerName: targetName, + playerId: target.player_id, + inviteLabel: gameManager.config.getWithoutLogs( + type+'/labels/inviteLabel', + TeamsConst.LABELS[type.toUpperCase()].INVITE_BUTTON_LABEL + ) + } + ) + ); + let inviteButton = gameManager.gameDom.getElement('.'+type+'-invite-' + target.player_id + ' button'); + inviteButton?.addEventListener('click', () => { + let sendInvite = {act: TeamsConst.ACTIONS[type.toUpperCase()+'_INVITE'], id: target.player_id}; + gameManager.room.send('*', sendInvite); + inviteButton.style.display = 'none'; + gameManager.gameEngine.clearTarget(); + }); + } + + static targetIsValidPlayer(target, currentPlayer) + { + return GameConst.TYPE_PLAYER === target.type && currentPlayer.playerId !== target.id; + } + + static playerBySessionId(currentClan, targetId) + { + let playersKeys = Object.keys(currentClan.players); + if(0 === playersKeys.length){ + return false; + } + for(let i of playersKeys){ + if(currentClan.players[i].sessionId === targetId){ + return currentClan.players[i]; + } + } + return false; + } + +} + +module.exports.TargetBoxEnricher = TargetBoxEnricher; diff --git a/lib/teams/client/team-message-handler.js b/lib/teams/client/team-message-handler.js new file mode 100644 index 000000000..8ad3597de --- /dev/null +++ b/lib/teams/client/team-message-handler.js @@ -0,0 +1,251 @@ +/** + * + * Reldens - TeamMessageHandler + * + */ + +const { UserInterface } = require('../../game/client/user-interface'); +const { TeamsConst } = require('../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class TeamMessageHandler +{ + + constructor(props) + { + this.roomEvents = sc.get(props, 'roomEvents', false); + this.message = sc.get(props, 'message', false); + this.gameManager = this.roomEvents?.gameManager; + this.gameDom = this.gameManager?.gameDom; + this.uiScene = this.gameManager?.gameEngine?.uiScene; + } + + validate() + { + if(!this.roomEvents){ + Logger.info('Missing RoomEvents on TeamMessageHandler.'); + return false; + } + if(!this.message){ + Logger.info('Missing message on TeamMessageHandler.'); + return false; + } + if(!this.gameManager){ + Logger.info('Missing GameManager on TeamMessageHandler.'); + return false; + } + // @NOTE: the message could arrive before the uiScene gets ready. + // if(!this.uiScene){ + // Logger.info('Missing UI Scene on TeamMessageHandler.'); + // } + return this.uiScene; + + } + + showTeamRequest() + { + this.createTeamUi(this.teamUiKey()); + this.roomEvents.initUi({ + id: this.teamUiKey(), + title: this.gameManager.config.getWithoutLogs( + 'client/team/labels/requestFromTitle', + TeamsConst.LABELS.TEAM.REQUEST_FROM + ), + content: this.message.from, + options: this.gameManager.config.get('client/ui/options/acceptOrDecline'), + overrideSendOptions: {act: TeamsConst.ACTIONS.TEAM_ACCEPTED, id: this.message.id} + }); + this.gameDom.getElement('#opt-2-'+this.teamUiKey())?.addEventListener('click', () => { + this.removeTeamUi(); + }); + } + + removeTeamUi() + { + let uiElement = this.gameManager.getUiElement(this.teamUiKey()); + if(!uiElement){ + Logger.error('UI Element not found by team UI key "'+this.teamUiKey()+'".'); + return false; + } + uiElement.removeElement(); + delete this.uiScene.userInterfaces[this.teamUiKey()]; + delete this.uiScene.elementsUi[this.teamUiKey()]; + this.uiScene.currentTeam = false; + } + + teamUiKey() + { + return TeamsConst.KEY + this.message.id; + } + + showTeamBox() + { + let teamUiKey = this.teamUiKey(); + this.createTeamUi(teamUiKey); + let title = this.gameManager.config.getWithoutLogs( + 'client/team/labels/leaderNameTitle', + TeamsConst.LABELS.TEAM.LEADER_NAME_TITLE + ).replace('%leaderName', this.message.leaderName); + let container = this.gameManager.gameDom.getElement('#box-'+teamUiKey+' .box-content'); + if(!container){ + Logger.error('Missing container: "#box-'+teamUiKey+' .box-content".'); + return false; + } + let uiBox = this.uiScene.elementsUi[teamUiKey]; + this.roomEvents.uiSetTitle(uiBox, {title}); + this.roomEvents.uiSetContent(uiBox, {content: ''}, this.uiScene); + let players = sc.get(this.message, 'players', false); + this.uiScene.currentTeam = players; + this.updateTeamBox(players, container); + } + + createTeamUi(teamUiKey) + { + let teamsUi = sc.get(this.uiScene.userInterfaces, teamUiKey); + if(teamsUi){ + return teamsUi; + } + if(!this.uiScene.userInterfaces){ + this.uiScene.userInterfaces = {}; + } + this.uiScene.userInterfaces[teamUiKey] = new UserInterface( + this.gameManager, + {id: teamUiKey, type: TeamsConst.KEY, defaultOpen: true, defaultClose: true}, + 'assets/features/teams/templates/ui-teams.html', + TeamsConst.KEY + ); + this.uiScene.userInterfaces[teamUiKey].createUiElement(this.uiScene, TeamsConst.KEY); + return this.uiScene.userInterfaces[teamUiKey]; + } + + updateTeamBox(players, container) + { + if(!players){ + Logger.error('Players not defined.', players); + return; + } + let teamMembers = ''; + for(let i of Object.keys(players)){ + teamMembers += this.createTeamMemberBox(players[i]); + } + container.innerHTML = this.createTeamContainer(teamMembers); + this.activateTeamPlayerActions(players); + this.activateTeamLeaveButtonAction(); + } + + createTeamContainer(teamMembers) + { + // @TODO - BETA - Move the template load from cache as part of the engine driver. + let templateContent = this.uiScene.cache.html.get('teamContainer'); + if(!templateContent){ + Logger.error('Missing template "teamContainer".'); + return ''; + } + let playerId = this.gameManager.playerData.id.toString(); + let isPlayerOwner = playerId === this.message.id.toString(); + let configPath = 'client/team/labels/'; + let leaveActionLabel = isPlayerOwner + ? this.gameManager.config.getWithoutLogs(configPath+'disbandLabel', TeamsConst.LABELS.TEAM.DISBAND) + : this.gameManager.config.getWithoutLogs(configPath+'leaveLabel', TeamsConst.LABELS.TEAM.LEAVE); + let templateParams = { + teamId: this.message.id, + playerId, + leaveActionLabel, + teamMembers + }; + return this.gameManager.gameEngine.parseTemplate(templateContent, templateParams); + } + + activateTeamLeaveButtonAction() + { + let leaveButton = this.gameManager.gameDom.getElement('.leave-'+this.message.id); + leaveButton?.addEventListener('click', () => { + this.gameManager.activeRoomEvents.room.send('*', { + act: TeamsConst.ACTIONS.TEAM_LEAVE, + id: this.message.id + }); + }); + } + + createTeamMemberBox(playerData) + { + // @TODO - BETA - Move the template load from cache as part of the engine driver. + let templateContent = this.uiScene.cache.html.get('teamPlayerData'); + if(!templateContent){ + Logger.error('Missing template "teamPlayerData".'); + return ''; + } + let isPlayerOwner = this.gameManager.playerData.id.toString() === this.message.id.toString(); + return this.gameManager.gameEngine.parseTemplate(templateContent, { + playerId: playerData.player_id, + playerName: playerData.name, + playerProperties: this.createSharedPropertiesContent(playerData.sharedProperties), + playerRemove: isPlayerOwner ? this.createDismissPlayerButton(playerData) : '' + }); + } + + createDismissPlayerButton(playerData) + { + let templateContent = this.uiScene.cache.html.get('teamRemove'); + if(!templateContent){ + Logger.error('Missing template "teamRemove".'); + return ''; + } + return this.gameManager.gameEngine.parseTemplate(templateContent, {playerId: playerData.player_id}); + } + + createSharedPropertiesContent(playerSharedProperties) + { + let templateContent = this.uiScene.cache.html.get('teamsSharedProperty'); + if(!templateContent){ + Logger.error('Missing template "teamsSharedProperty".'); + return ''; + } + let sharedPropertiesContent = ''; + // @TODO - BETA - Move the template load from cache as part of the engine driver. + for(let i of Object.keys(playerSharedProperties)){ + templateContent = this.uiScene.cache.html.get('teamsSharedProperty'); + let propertyData = playerSharedProperties[i]; + let propertyMaxValue = sc.get(propertyData, 'max', ''); + if('' !== propertyMaxValue){ + propertyMaxValue = this.gameManager.config.getWithoutLogs( + 'client/team/labels/propertyMaxValue', + TeamsConst.LABELS.TEAM.PROPERTY_MAX_VALUE + ).replace('%propertyMaxValue', propertyMaxValue); + } + sharedPropertiesContent += this.gameManager.gameEngine.parseTemplate(templateContent, { + key: i, + label: propertyData.label, + value: propertyData.value, + max: propertyMaxValue + }); + } + return sharedPropertiesContent; + } + + activateTeamPlayerActions(playersData) + { + for(let i of Object.keys(playersData)){ + let playerData = playersData[i]; + let selectorPlayerName = '.team-player-'+playerData.player_id+' .player-name'; + this.gameDom.getElement(selectorPlayerName)?.addEventListener('click', () => { + this.gameManager.getCurrentPlayer().setTargetPlayerById(playerData.sessionId); + }); + let selectorPlayerProperties = '.team-player-'+playerData.player_id+' .properties-list-container'; + this.gameDom.getElement(selectorPlayerProperties)?.addEventListener('click', () => { + this.gameManager.getCurrentPlayer().setTargetPlayerById(playerData.sessionId); + }); + let selectorPlayerRemove = '.team-player-'+playerData.player_id+' .team-remove-button'; + this.gameDom.getElement(selectorPlayerRemove)?.addEventListener('click', () => { + this.gameManager.activeRoomEvents.room.send('*', { + act: TeamsConst.ACTIONS.TEAM_REMOVE, + id: this.message.id, + remove: playerData.player_id + }); + }); + } + } + +} + +module.exports.TeamMessageHandler = TeamMessageHandler; diff --git a/lib/teams/client/team-message-listener.js b/lib/teams/client/team-message-listener.js new file mode 100644 index 000000000..efdbf540e --- /dev/null +++ b/lib/teams/client/team-message-listener.js @@ -0,0 +1,63 @@ +/** + * + * Reldens - TeamMessageListener + * + */ + +const { TeamMessageHandler } = require('./team-message-handler'); +const { TeamsConst } = require('../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class TeamMessageListener +{ + + async executeClientMessageActions(props) + { + let message = sc.get(props, 'message', false); + if(!message){ + Logger.error('Missing message data on TeamMessageListener.', props); + return false; + } + let roomEvents = sc.get(props, 'roomEvents', false); + if(!roomEvents){ + Logger.error('Missing RoomEvents on TeamMessageListener.', props); + return false; + } + let teamMessageHandler = new TeamMessageHandler({roomEvents, message}); + if(!teamMessageHandler.validate()){ + if(this.isTeamMessage(message)){ + if(!roomEvents.teamMessagesQueue){ + roomEvents.teamMessagesQueue = []; + } + roomEvents.teamMessagesQueue.push(message); + } + Logger.error('Invalid TeamMessageHandler', teamMessageHandler); + return false; + } + if(!this.isTeamMessage(message)){ + return false; + } + return this.handleTeamMessage(message, teamMessageHandler); + } + + handleTeamMessage(message, teamMessageHandler) + { + if(TeamsConst.ACTIONS.TEAM_INVITE === message.act){ + return teamMessageHandler.showTeamRequest(); + } + if(TeamsConst.ACTIONS.TEAM_UPDATE === message.act){ + return teamMessageHandler.showTeamBox(); + } + if(TeamsConst.ACTIONS.TEAM_LEFT === message.act){ + return teamMessageHandler.removeTeamUi(); + } + return true; + } + + isTeamMessage(message) + { + return 0 === message.act.indexOf(TeamsConst.TEAM_PREF); + } +} + +module.exports.TeamMessageListener = TeamMessageListener; diff --git a/lib/teams/client/templates-handler.js b/lib/teams/client/templates-handler.js new file mode 100644 index 000000000..1fe432939 --- /dev/null +++ b/lib/teams/client/templates-handler.js @@ -0,0 +1,33 @@ +/** + * + * Reldens - TemplatesHandler + * + */ + +const { TeamsConst } = require('../constants'); + +class TemplatesHandler +{ + + static preloadTemplates(preloadScene) + { + let teamsTemplatePath = 'assets/features/teams/templates/'; + preloadScene.load.html(TeamsConst.KEY, teamsTemplatePath+'ui-teams.html'); + preloadScene.load.html(TeamsConst.CLAN_KEY, teamsTemplatePath+'ui-clan.html'); + preloadScene.load.html('teamPlayerInvite', teamsTemplatePath+'team-invite.html'); + preloadScene.load.html('teamPlayerAccept', teamsTemplatePath+'team-accept.html'); + preloadScene.load.html('teamRemove', teamsTemplatePath+'team-remove.html'); + preloadScene.load.html('teamContainer', teamsTemplatePath+'team-container.html'); + preloadScene.load.html('teamPlayerData', teamsTemplatePath+'team-player-data.html'); + preloadScene.load.html('clanCreate', teamsTemplatePath+'clan-create.html'); + preloadScene.load.html('clanPlayerInvite', teamsTemplatePath+'clan-invite.html'); + preloadScene.load.html('clanPlayerAccept', teamsTemplatePath+'clan-accept.html'); + preloadScene.load.html('clanRemove', teamsTemplatePath+'clan-remove.html'); + preloadScene.load.html('clanContainer', teamsTemplatePath+'clan-container.html'); + preloadScene.load.html('clanPlayerData', teamsTemplatePath+'clan-player-data.html'); + preloadScene.load.html('clanMemberData', teamsTemplatePath+'clan-member-data.html'); + preloadScene.load.html('teamsSharedProperty', teamsTemplatePath+'shared-property.html'); + } + +} +module.exports.TemplatesHandler = TemplatesHandler; \ No newline at end of file diff --git a/lib/teams/constants.js b/lib/teams/constants.js new file mode 100644 index 000000000..f6b718f9a --- /dev/null +++ b/lib/teams/constants.js @@ -0,0 +1,79 @@ +/** + * + * Reldens - teams/constants + * + */ + +let pref = 'tm.' +let clanPref = 'cln.'; + +module.exports.TeamsConst = { + KEY: 'teams', + CLAN_KEY: 'clan', + TEAM_PREF: pref, + CLAN_PREF: clanPref, + NAME_LIMIT: 50, + CLAN_STARTING_POINTS: 1, + VALIDATION: { + SUCCESS: 1, + NAME_EXISTS: 2, + LEVEL_ISSUE: 3, + CREATE_ERROR: 4, + CREATE_OWNER_ERROR: 5 + }, + ACTIONS: { + // @TODO - BETA - Standardize generic actions and use dots to split, like UPDATE = '.up', REMOVE = '.rm', etc. + TEAM_INVITE: pref+'inv', + TEAM_ACCEPTED: pref+'acp', + TEAM_LEAVE: pref+'lev', + TEAM_UPDATE: pref+'upd', + TEAM_LEFT: pref+'lef', + TEAM_REMOVE: pref+'rem', + CLAN_INITIALIZE: clanPref+'ini', + CLAN_CREATE: clanPref+'new', + CLAN_INVITE: clanPref+'inv', + CLAN_ACCEPTED: clanPref+'acp', + CLAN_LEAVE: clanPref+'lev', + CLAN_UPDATE: clanPref+'upd', + CLAN_LEFT: clanPref+'lef', + CLAN_REMOVE: clanPref+'rem', + CLAN_REMOVED: clanPref+'remd', + CLAN_NAME: clanPref+'nam' + }, + LABELS: { + TEAM: { + INVITE_BUTTON_LABEL: 'Team - Invite', + REQUEST_FROM: 'Accept team request from:', + LEADER_NAME_TITLE: 'Team leader: %leaderName', + DISBAND: 'Disband Team', + LEAVE: 'Leave Team', + PROPERTY_MAX_VALUE: '/ %propertyMaxValue' + }, + CLAN: { + CREATE_CLAN_TITLE: 'Clan - Creation', + INVITE_BUTTON_LABEL: 'Clan - Invite', + REQUEST_FROM: 'Accept clan request from:', + CLAN_TITLE: 'Clan: %clanName - Leader: %leaderName', + NAME_PLACEHOLDER: 'Choose a clan name...', + CREATE: 'Create', + DISBAND: 'Disband Clan', + LEAVE: 'Leave Clan', + PROPERTY_MAX_VALUE: '/ %propertyMaxValue', + PLAYERS_TITLE: 'Connected Players:', + MEMBERS_TITLE: 'Clan Members:', + NONE_CONNECTED: 'None' + } + }, + CHAT: { + MESSAGE: { + INVITE_ACCEPTED: '%playerName has accepted your invitation.', + INVITE_REJECTED: '%playerName has rejected your invitation.', + DISBANDED: '%playerName has disbanded the %groupName.', + LEFT: 'You left the %groupName.', + LEAVE: '%playerName has left the %groupName.', + REMOVED: '%playerName has been removed from the %groupName.', + ENTER: '%playerName has enter the %groupName.', + NOT_ENOUGH_PLAYERS: 'The team was disbanded due to a lack of players.' + } + } +}; diff --git a/lib/teams/server/clan-factory.js b/lib/teams/server/clan-factory.js new file mode 100644 index 000000000..9ee287d83 --- /dev/null +++ b/lib/teams/server/clan-factory.js @@ -0,0 +1,37 @@ +/** + * + * Reldens - ClanFactory + * + */ + +const { Clan } = require('./clan'); +const { Logger } = require('@reldens/utils'); + +class ClanFactory +{ + + static async create(clanId, playerOwner, clientOwner, sharedProperties, teamsPlugin) + { + // @TODO - BETA - Refactor to extract the teamsPlugin. + let clanModel = await teamsPlugin.dataServer.getEntity('clan').loadByIdWithRelations( + clanId, + ['player_owner', 'members.parent_player, parent_level.modifiers'] + ); + if(!clanModel){ + Logger.error('Clan not found by ID "'+clanId+'".'); + return false; + } + // @TODO - BETA - Refactor fromModel and join methods to use a single one. + let newClan = Clan.fromModel({ + clanModel, + clientOwner, + sharedProperties + }); + newClan.join(playerOwner, clientOwner, newClan.members[playerOwner.player_id]); + teamsPlugin.clans[clanId] = newClan; + return teamsPlugin.clans[clanId]; + } + +} + +module.exports.ClanFactory = ClanFactory; diff --git a/lib/teams/server/clan-message-actions.js b/lib/teams/server/clan-message-actions.js new file mode 100644 index 000000000..37dc0172e --- /dev/null +++ b/lib/teams/server/clan-message-actions.js @@ -0,0 +1,69 @@ +/** + * + * Reldens - ClanMessageActions + * + */ + +const { ClanCreate } = require('./message-actions/clan-create'); +const { TryClanInvite } = require('./message-actions/try-clan-invite'); +const { ClanJoin } = require('./message-actions/clan-join'); +const { ClanLeave } = require('./message-actions/clan-leave'); +const { TeamsConst } = require('../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class ClanMessageActions +{ + + constructor(props) + { + this.teamsPlugin = props.teamsPlugin; + } + + async executeMessageActions(client, data, room, playerSchema) + { + if(!sc.hasOwn(data, 'act')){ + return false; + } + if(0 !== data.act.indexOf(TeamsConst.CLAN_PREF)){ + return false; + } + if(TeamsConst.ACTIONS.CLAN_INVITE === data.act){ + return await TryClanInvite.execute(client, data, room, playerSchema, this.teamsPlugin); + } + if(TeamsConst.ACTIONS.CLAN_CREATE === data.act){ + return await ClanCreate.execute(client, data, room, playerSchema, this.teamsPlugin); + } + if(TeamsConst.ACTIONS.CLAN_ACCEPTED === data.act && '1' === data.value){ + return await ClanJoin.execute(client, data, room, playerSchema, this.teamsPlugin); + } + if(TeamsConst.ACTIONS.CLAN_ACCEPTED === data.act && '2' === data.value){ + let clanInvite = this.teamsPlugin.clans[data.id]; + if(!clanInvite){ + Logger.error('Invite Clan not found.', clanInvite, data); + return false; + } + let clientSendingInvite = clanInvite.clients[clanInvite.owner.player_id]; + if(!clientSendingInvite){ + Logger.error( + 'Clan invitation declined, player owner client not found.', + clanInvite, + clientSendingInvite, + data + ); + return false; + } + let playerRejectingName = playerSchema.playerName; + await this.teamsPlugin.events.emit('reldens.clanJoinInviteRejected', { + clientSendingInvite, + playerRejectingName, + clanInvite + }); + } + if(TeamsConst.ACTIONS.CLAN_LEAVE === data.act || TeamsConst.ACTIONS.CLAN_REMOVE === data.act){ + return await ClanLeave.fromMessage(data, playerSchema, this.teamsPlugin); + } + } + +} + +module.exports.ClanMessageActions = ClanMessageActions; diff --git a/lib/teams/server/clan-updates-handler.js b/lib/teams/server/clan-updates-handler.js new file mode 100644 index 000000000..faf535fde --- /dev/null +++ b/lib/teams/server/clan-updates-handler.js @@ -0,0 +1,56 @@ +/** + * + * Reldens - ClanUpdatesHandler + * + */ + +const { PlayersDataMapper } = require('./players-data-mapper'); +const { TeamsConst } = require('../constants'); +const { Logger } = require('@reldens/utils'); + +class ClanUpdatesHandler +{ + + static updateClanPlayers(clan) + { + // @TODO - BETA - Consider extend TeamUpdatesHandler. + let clientsKeys = Object.keys(clan.clients); + if(0 === clientsKeys.length){ + Logger.info('Clan update without clients.', clan); + return false; + } + let playersList = PlayersDataMapper.fetchPlayersData(clan); + if(0 === Object.keys(playersList).length){ + Logger.info('Clan update without players.', clan); + return false; + } + for(let i of clientsKeys){ + let otherPlayersData = Object.assign({}, playersList); + delete otherPlayersData[i]; + let sendUpdate = Object.assign( + {}, + { + act: this.actionConstant(), + listener: this.listenerKey(), + players: otherPlayersData, + }, + clan.mapForClient() + ); + clan.clients[i].send('*', sendUpdate); + } + return true; + } + + static listenerKey() + { + return TeamsConst.CLAN_KEY; + } + + static actionConstant() + { + return TeamsConst.ACTIONS.CLAN_UPDATE; + } + +} + +module.exports.ClanUpdatesHandler = ClanUpdatesHandler; diff --git a/lib/teams/server/clan.js b/lib/teams/server/clan.js new file mode 100644 index 000000000..f9877ccf7 --- /dev/null +++ b/lib/teams/server/clan.js @@ -0,0 +1,172 @@ +/** + * + * Reldens - Clan + * + */ + +const { ModifierConst } = require('@reldens/modifiers'); +const { Logger, sc } = require('@reldens/utils'); + +class Clan +{ + constructor(props) + { + this.level = sc.get(props, 'level', 1); + this.modifiers = sc.get(props, 'modifiers', {}); + this.sharedProperties = sc.get(props, 'sharedProperties', {}); + this.owner = sc.get(props, 'owner', false); + this.ownerClient = sc.get(props, 'ownerClient', false); + this.players = sc.get(props, 'players', {}); + this.clients = sc.get(props, 'clients', {}); + this.id = sc.get(props, 'id', ''); + this.name = sc.get(props, 'name', ''); + this.points = sc.get(props, 'points', ''); + this.members = sc.get(props, 'members', {}); + this.pendingInvites = sc.get(props, 'pendingInvites', {}); + } + + static fromModel(props) + { + let {clanModel, sharedProperties, clientOwner} = props; + return new this({ + sharedProperties, + owner: { + player_id: clanModel.player_owner.id, + playerName: clanModel.player_owner.name + }, + ownerClient: clientOwner, + members: this.mapMembersFromModelArray(clanModel.members), + id: clanModel.id, + ownerId: clanModel.owner_id, + name: clanModel.name, + points: clanModel.points, + level: clanModel.level + }); + } + + static mapMembersFromModelArray(membersCollection) + { + if(!sc.isArray(membersCollection) || 0 === membersCollection.length){ + return {}; + } + let mappedMembers = {}; + for(let memberModel of membersCollection){ + mappedMembers[memberModel.player_id] = memberModel; + } + return mappedMembers; + } + + mapForClient() + { + return { + id: this.id, + clanName: this.name, + points: this.points, + level: this.level, + ownerId: this.owner.player_id, + leaderName: this.owner.playerName, + members: this.mapMembersForClient() + }; + } + + mapMembersForClient() + { + if(0 === sc.length(this.members)){ + return []; + } + let members = {}; + for(let i of Object.keys(this.members)){ + let member = this.members[i]; + let parentPlayer = member?.parent_player; + if(!parentPlayer){ + Logger.error('Member player not available.', member); + continue; + } + members[i] = { + // @TODO - BETA - Make other members data optional, like level or class path. + name: parentPlayer.name, + id: i + } + } + return members; + } + + join(playerSchema, client, clanMemberModel) + { + let result = this.applyModifiers(playerSchema); + if(!result){ + Logger.error('Player modifiers could not be applied.', playerSchema.player_id, result, this.players); + } + this.players[playerSchema.player_id] = playerSchema; + this.clients[playerSchema.player_id] = client; + this.members[playerSchema.player_id] = clanMemberModel; + return result; + } + + leave(playerSchema) + { + delete this.members[playerSchema.player_id]; + return this.disconnect(playerSchema); + } + + disconnect(playerSchema) + { + let result = this.revertModifiers(playerSchema); + if(!result){ + Logger.error('Player modifiers could not be reverted.', playerSchema.player_id, result, this.players); + } + playerSchema.privateData.clan = false; + delete this.clients[playerSchema.player_id]; + delete this.players[playerSchema.player_id]; + return true; + } + + playerBySessionId(sessionId) + { + let playersKeys = Object.keys(this.players); + if(0 === playersKeys.length){ + return false; + } + for(let i of playersKeys){ + if(this.players[i].sessionId === sessionId){ + return this.players[i]; + } + } + return false; + } + + applyModifiers(playerSchema) + { + let modifiersKeys = Object.keys(this.modifiers); + if(0 === modifiersKeys.length){ + return true; + } + for(let i of modifiersKeys){ + let modifier = this.modifiers[i]; + modifier.apply(playerSchema); + if(ModifierConst.MOD_APPLIED !== modifier.state){ + return false; + } + } + return true; + } + + revertModifiers(playerSchema) + { + let modifiersKeys = Object.keys(this.modifiers); + if(0 === modifiersKeys.length){ + return true; + } + for(let i of modifiersKeys){ + let modifier = this.modifiers[i]; + modifier.revert(playerSchema); + if(ModifierConst.MOD_REVERTED !== modifier.state){ + return false; + } + } + return true; + } + +} + +module.exports.Clan = Clan; \ No newline at end of file diff --git a/lib/teams/server/entities-config.js b/lib/teams/server/entities-config.js new file mode 100644 index 000000000..49d6cbd17 --- /dev/null +++ b/lib/teams/server/entities-config.js @@ -0,0 +1,24 @@ +/** + * + * Reldens - Entities Config + * + */ + +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'); + +let objectsConfig = { + parentItemLabel: 'Clan', + icon: 'User' +}; + +let entitiesConfig = { + clan: ClanEntity.propertiesConfig(objectsConfig), + clanLevels: ClanLevelsEntity.propertiesConfig(objectsConfig), + clanLevelsModifiers: ClanLevelsModifiersEntity.propertiesConfig(objectsConfig), + clanMembers: ClanMembersEntity.propertiesConfig(objectsConfig) +}; + +module.exports.entitiesConfig = entitiesConfig; diff --git a/lib/teams/server/entities-translations.js b/lib/teams/server/entities-translations.js new file mode 100644 index 000000000..af88e0162 --- /dev/null +++ b/lib/teams/server/entities-translations.js @@ -0,0 +1,14 @@ +/** + * + * Reldens - Entities Translations + * + */ + +module.exports.entitiesTranslations = { + labels: { + clan: 'Clan', + clan_levels: 'Levels', + clan_levels_modifiers: 'Levels Modifiers', + clan_members: 'Members' + } +}; diff --git a/lib/teams/server/entities/clan-entity.js b/lib/teams/server/entities/clan-entity.js new file mode 100644 index 000000000..f7ed95642 --- /dev/null +++ b/lib/teams/server/entities/clan-entity.js @@ -0,0 +1,45 @@ +/** + * + * Reldens - ClanEntity + * + */ + +const { EntityProperties } = require('../../../game/server/entity-properties'); + +class ClanEntity extends EntityProperties +{ + + static propertiesConfig(extraProps) + { + let properties = { + id: {}, + owner_id: { + type: 'reference', + reference: 'players', + isRequired: true + }, + name: { + isRequired: true + }, + points: { + type: 'number' + }, + level: {} + }; + + let listPropertiesKeys = Object.keys(properties); + let editPropertiesKeys = [...listPropertiesKeys]; + editPropertiesKeys.splice(editPropertiesKeys.indexOf('id'), 1); + + return Object.assign({ + listProperties: listPropertiesKeys, + showProperties: Object.keys(properties), + filterProperties: listPropertiesKeys, + editProperties: editPropertiesKeys, + properties + }, extraProps); + } + +} + +module.exports.ClanEntity = ClanEntity; diff --git a/lib/teams/server/entities/clan-levels-entity.js b/lib/teams/server/entities/clan-levels-entity.js new file mode 100644 index 000000000..841f4120a --- /dev/null +++ b/lib/teams/server/entities/clan-levels-entity.js @@ -0,0 +1,44 @@ +/** + * + * Reldens - ClanLevelsEntity + * + */ + +const { EntityProperties } = require('../../../game/server/entity-properties'); + +class ClanLevelsEntity extends EntityProperties +{ + + static propertiesConfig(extraProps) + { + let properties = { + id: {}, + key: { + type: 'number', + isRequired: true + }, + label: { + isRequired: true + }, + required_experience: { + type: 'number', + isRequired: true + } + }; + + let listPropertiesKeys = Object.keys(properties); + let editPropertiesKeys = [...listPropertiesKeys]; + editPropertiesKeys.splice(editPropertiesKeys.indexOf('id'), 1); + + return Object.assign({ + listProperties: listPropertiesKeys, + showProperties: Object.keys(properties), + filterProperties: listPropertiesKeys, + editProperties: editPropertiesKeys, + properties + }, extraProps); + } + +} + +module.exports.ClanLevelsEntity = ClanLevelsEntity; diff --git a/lib/teams/server/entities/clan-levels-modifiers-entity.js b/lib/teams/server/entities/clan-levels-modifiers-entity.js new file mode 100644 index 000000000..fd652cb0d --- /dev/null +++ b/lib/teams/server/entities/clan-levels-modifiers-entity.js @@ -0,0 +1,75 @@ +/** + * + * Reldens - ClanLevelsModifiersEntity + * + */ + +const { EntityProperties } = require('../../../game/server/entity-properties'); +const { sc } = require('@reldens/utils'); + +class ClanLevelsModifiersEntity extends EntityProperties +{ + + static propertiesConfig(extraProps) + { + let properties = { + id: {}, + level_id: { + type: 'reference', + reference: 'clan_levels', + isRequired: true + }, + key: { + isTitle: true, + isRequired: true + }, + property_key: { + isRequired: true + }, + operation: { + availableValues: [ + {value: 1, label: 'Increment'}, + {value: 2, label: 'Decrease'}, + {value: 3, label: 'Divide'}, + {value: 4, label: 'Multiply'}, + {value: 5, label: 'Increment Percentage'}, + {value: 6, label: 'Decrease Percentage'}, + {value: 7, label: 'Set'}, + {value: 8, label: 'Method'}, + {value: 9, label: 'Set Number'} + ], + isRequired: true + }, + value: { + isRequired: true + }, + minValue: {}, + maxValue: {}, + minProperty: {}, + maxProperty: {}, + }; + + let listPropertiesKeys = Object.keys(properties); + let editPropertiesKeys = [...listPropertiesKeys]; + + listPropertiesKeys = sc.removeFromArray(listPropertiesKeys, [ + 'minValue', + 'maxValue', + 'minProperty', + 'maxProperty' + ]); + editPropertiesKeys.splice(editPropertiesKeys.indexOf('id'), 1); + + return { + listProperties: listPropertiesKeys, + showProperties: Object.keys(properties), + filterProperties: listPropertiesKeys, + editProperties: editPropertiesKeys, + properties, + ...extraProps + }; + } + +} + +module.exports.ClanLevelsModifiersEntity = ClanLevelsModifiersEntity; diff --git a/lib/teams/server/entities/clan-members-entity.js b/lib/teams/server/entities/clan-members-entity.js new file mode 100644 index 000000000..336f7ebc1 --- /dev/null +++ b/lib/teams/server/entities/clan-members-entity.js @@ -0,0 +1,43 @@ +/** + * + * Reldens - ClanMembersEntity + * + */ + +const { EntityProperties } = require('../../../game/server/entity-properties'); + +class ClanMembersEntity extends EntityProperties +{ + + static propertiesConfig(extraProps) + { + let properties = { + id: {}, + clan_id: { + type: 'reference', + reference: 'clan', + isRequired: true + }, + player_id: { + type: 'reference', + reference: 'players', + isRequired: true + }, + }; + + let listPropertiesKeys = Object.keys(properties); + let editPropertiesKeys = [...listPropertiesKeys]; + editPropertiesKeys.splice(editPropertiesKeys.indexOf('id'), 1); + + return Object.assign({ + listProperties: listPropertiesKeys, + showProperties: Object.keys(properties), + filterProperties: listPropertiesKeys, + editProperties: editPropertiesKeys, + properties + }, extraProps); + } + +} + +module.exports.ClanMembersEntity = ClanMembersEntity; diff --git a/lib/teams/server/event-handlers/create-player-clan-handler.js b/lib/teams/server/event-handlers/create-player-clan-handler.js new file mode 100644 index 000000000..99916c741 --- /dev/null +++ b/lib/teams/server/event-handlers/create-player-clan-handler.js @@ -0,0 +1,70 @@ +/** + * + * Reldens - CreatePlayerClanHandler + * + */ + +const { ClanFactory } = require('../clan-factory'); +const { ClanUpdatesHandler } = require('../clan-updates-handler'); +const { TeamsConst } = require('../../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class CreatePlayerClanHandler +{ + + static async enrichPlayerWithClan(client, playerSchema, room, teamsPlugin) + { + let startEvent = {client, playerSchema, room, teamsPlugin, continueProcess: true}; + teamsPlugin.events.emit('reldens.beforeEnrichPlayerWithClan', startEvent); + if(!startEvent.continueProcess){ + return false; + } + let clanMemberModel = await teamsPlugin.dataServer.getEntity('clanMembers').loadOneByWithRelations( + 'player_id', + playerSchema.player_id, + ['parent_player'] + ); + if(!clanMemberModel){ + let sendData = { + act: TeamsConst.ACTIONS.CLAN_INITIALIZE, + listener: TeamsConst.CLAN_KEY + }; + client.send('*', sendData); + return true; + } + let clan = await this.loadClan(teamsPlugin, clanMemberModel.clan_id, playerSchema, client, room); + if(!clan){ + Logger.error('Loading clan by ID "'+clanMemberModel.clan_id+'" not found.', clan); + return false; + } + clan.join(playerSchema, client, clanMemberModel); + playerSchema.setPrivate('clan', clanMemberModel.clan_id); + let endEvent = {client, playerSchema, room, teamsPlugin, continueProcess: true}; + teamsPlugin.events.emit('reldens.beforeEnrichPlayerWithClanUpdate', endEvent); + if(!endEvent.continueProcess){ + return false; + } + return ClanUpdatesHandler.updateClanPlayers(clan); + } + + static async loadClan(teamsPlugin, clanId, playerSchema, client, room) + { + let clan = sc.get(teamsPlugin.clans, clanId, false); + if(clan){ + Logger.info('Loading clan from plugin with ID "'+clanId+'".'); + } + if(!clan){ + Logger.info('Clan with ID "'+clanId+'" not loaded, creating object.'); + return await ClanFactory.create( + clanId, + playerSchema, + client, + room.config.get('client/ui/teams/sharedProperties'), + teamsPlugin + ); + } + return clan; + } +} + +module.exports.CreatePlayerClanHandler = CreatePlayerClanHandler; diff --git a/lib/teams/server/event-handlers/create-player-team-handler.js b/lib/teams/server/event-handlers/create-player-team-handler.js new file mode 100644 index 000000000..e3b446a52 --- /dev/null +++ b/lib/teams/server/event-handlers/create-player-team-handler.js @@ -0,0 +1,33 @@ +/** + * + * Reldens - CreatePlayerTeamHandler + * + */ + +const { TeamUpdatesHandler } = require('../team-updates-handler'); +const { Logger } = require('@reldens/utils'); + +class CreatePlayerTeamHandler +{ + + static async joinExistentTeam(client, playerSchema, teamsPlugin) + { + let teamId = teamsPlugin.teamChangingRoomPlayers[playerSchema.player_id]?.teamId; + if(!teamId){ + Logger.info('Player "'+playerSchema.playerName+'" (ID "'+playerSchema.player_id+'"), is not in a team.'); + return false; + } + let currentTeam = teamsPlugin.teams[teamId]; + if(!currentTeam){ + Logger.error('Player "'+playerSchema.player_id+'" current team "'+teamId+'"not found.'); + playerSchema.currentTeam = false; + return false; + } + currentTeam.join(playerSchema, client); + playerSchema.currentTeam = teamId; + TeamUpdatesHandler.updateTeamPlayers(currentTeam); + } + +} + +module.exports.CreatePlayerTeamHandler = CreatePlayerTeamHandler; diff --git a/lib/teams/server/event-handlers/end-player-hit-change-point-team-handler.js b/lib/teams/server/event-handlers/end-player-hit-change-point-team-handler.js new file mode 100644 index 000000000..bb1c33b3b --- /dev/null +++ b/lib/teams/server/event-handlers/end-player-hit-change-point-team-handler.js @@ -0,0 +1,28 @@ +/** + * + * Reldens - EndPlayerHitChangePointTeamHandler + * + */ + +const { Logger } = require('@reldens/utils'); + +class EndPlayerHitChangePointTeamHandler +{ + + static async savePlayerTeam(playerSchema, teamsPlugin) + { + let teamId = playerSchema.currentTeam; + if(!teamId){ + Logger.info('Player "'+playerSchema?.playerName+'" (ID "'+playerSchema?.player_id+'"), team not saved.'); + return false; + } + teamsPlugin.teamChangingRoomPlayers[playerSchema.player_id] = { + teamId, + leavingPlayerSessionId: playerSchema.sessionId + }; + teamsPlugin.teams[teamId].leave(playerSchema); + } + +} + +module.exports.EndPlayerHitChangePointTeamHandler = EndPlayerHitChangePointTeamHandler; diff --git a/lib/teams/server/event-handlers/stats-update-handler.js b/lib/teams/server/event-handlers/stats-update-handler.js new file mode 100644 index 000000000..60c87efc1 --- /dev/null +++ b/lib/teams/server/event-handlers/stats-update-handler.js @@ -0,0 +1,45 @@ +/** + * + * Reldens - StatsUpdateHandler + * + */ + +const { ClanUpdatesHandler } = require('../clan-updates-handler'); +const { TeamUpdatesHandler } = require('../team-updates-handler'); +const { Logger, sc } = require('@reldens/utils'); + + +class StatsUpdateHandler +{ + + static async updateTeam(props) + { + let currentTeamId = sc.get(props.playerSchema, 'currentTeam', ''); + if('' === currentTeamId){ + return false; + } + let currentTeam = sc.get(props.teamsPlugin.teams, currentTeamId, false); + if(!currentTeam){ + Logger.error('Team not found: '+ currentTeamId); + return false; + } + return TeamUpdatesHandler.updateTeamPlayers(currentTeam); + } + + static async updateClan(props) + { + let clanId = props.playerSchema?.privateData?.clan; + if(!clanId){ + return false; + } + let clan = sc.get(props.teamsPlugin.clans, clanId, false); + if(!clan){ + Logger.error('Clan not found: '+ clanId); + return false; + } + return ClanUpdatesHandler.updateClanPlayers(clan); + } + +} + +module.exports.StatsUpdateHandler = StatsUpdateHandler; diff --git a/lib/teams/server/message-actions/chat-message-actions.js b/lib/teams/server/message-actions/chat-message-actions.js new file mode 100644 index 000000000..758d1f96d --- /dev/null +++ b/lib/teams/server/message-actions/chat-message-actions.js @@ -0,0 +1,285 @@ +/** + * + * Reldens - ChatMessageActions + * + */ + +const { ChatConst } = require('../../../chat/constants'); +const { TeamsConst } = require('../../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class ChatMessageActions +{ + + constructor(props) + { + this.events = sc.get(props, 'events', false); + this.chatPlugin = sc.get(props, 'chatPlugin', false); + this.client = false; + } + + listenEvents() + { + if(!this.events){ + Logger.error('EventsManager undefined in Teams ChatMessageActions.'); + return false; + } + if(!this.chatPlugin){ + Logger.error('ChatPlugin undefined in Teams ChatMessageActions.'); + return false; + } + this.inviteTeamAcceptedEventListener(); + this.inviteTeamRejectedEventListener(); + this.teamMemberLeaveEventListener(); + this.inviteClanAcceptedEventListener(); + this.inviteClanRejectedEventListener(); + this.clanMemberLeavingEventListener(); + } + + inviteTeamAcceptedEventListener() + { + this.events.on('reldens.afterPlayerJoinedTeam', (props) => { + let playerJoining = sc.get(props, 'playerJoining', false); + if(!playerJoining){ + Logger.warning('Missing event property "playerJoining".'); + return false; + } + let currentTeam = sc.get(props, 'currentTeam', false); + if(!currentTeam){ + Logger.warning('Missing event property "currentTeam".'); + return false; + } + let message = this.createMessage(TeamsConst.CHAT.MESSAGE.INVITE_ACCEPTED, playerJoining.playerName, 'team'); + this.sendMessage( + message, + 'Team', + currentTeam.ownerClient, + playerJoining.player_id, + playerJoining.state.room_id + ); + let messageEnter = this.createMessage(TeamsConst.CHAT.MESSAGE.ENTER, playerJoining.playerName, 'team'); + let otherClients = Object.assign({}, currentTeam.clients); + delete otherClients[playerJoining.player_id]; + delete otherClients[currentTeam.owner.player_id]; + for(let i of Object.keys(otherClients)){ + let player = currentTeam.players[i]; + this.sendMessage(messageEnter, 'Team', otherClients[i], player.player_id, player.state.room_id); + } + }); + } + + inviteTeamRejectedEventListener() + { + this.events.on('reldens.teamJoinInviteRejected', (props) => { + let activePlayerSendingInvite = sc.get(props, 'activePlayerSendingInvite', false); + let playerRejectingName = sc.get(props, 'playerRejectingName', false); + if(!activePlayerSendingInvite || !playerRejectingName){ + return false; + } + let message = this.createMessage(TeamsConst.CHAT.MESSAGE.INVITE_REJECTED, playerRejectingName, 'team'); + this.sendMessage( + message, + 'Team', + activePlayerSendingInvite.client, + activePlayerSendingInvite.playerData.id, + activePlayerSendingInvite.playerData.state.room_id + ); + }); + } + + teamMemberLeaveEventListener() + { + this.events.on('reldens.teamLeaveBeforeSendUpdate', (props) => { + let currentTeam = sc.get(props, 'currentTeam', false); + if(!currentTeam){ + return false; + } + let removingPlayerId = sc.get(props, 'playerId', false); + if(!removingPlayerId){ + return false; + } + let leavingPlayerId = sc.get(props, 'singleRemoveId', false); + if(!leavingPlayerId){ + return false; + } + let isOwnerDisbanding = sc.get(props, 'isOwnerDisbanding', false); + let areLessPlayerThanRequired = sc.get(props, 'areLessPlayerThanRequired', false); + if(isOwnerDisbanding){ + let room = sc.get(props, 'room', false); + if(!room){ + Logger.warning('Room undefined on "teamLeaveBeforeSendUpdate" event.'); + return false; + } + let message = this.createMessage( + TeamsConst.CHAT.MESSAGE.DISBANDED, + currentTeam.owner.playerName, + 'team' + ); + let leavingPlayer = room.fetchActivePlayerById(removingPlayerId); + if(!leavingPlayer){ + Logger.warning('Leaving team player width ID "'+removingPlayerId+'" not found.'); + return false; + } + this.sendMessage( + message, + 'Team', + leavingPlayer.client, + removingPlayerId, + leavingPlayer.playerData.state.room_id + ); + return true; + } + let room = sc.get(props, 'room', false); + if(!room){ + Logger.warning('Undefined room on teamLeaveBeforeSendUpdate event.'); + return false; + } + let leavingPlayer = room.fetchActivePlayerById(leavingPlayerId); + if(!leavingPlayer){ + Logger.info('Leaving team player with ID "'+leavingPlayerId+'" not found.'); + return false; + } + let message = this.createMessage(TeamsConst.CHAT.MESSAGE.LEFT, '', 'team'); + let otherClients = Object.assign({}, currentTeam.clients); + delete otherClients[leavingPlayerId]; + for(let i of Object.keys(otherClients)){ + let player = currentTeam.players[i]; + this.sendMessage(message, 'Team', otherClients[i], player.player_id, player.state.room_id); + } + if(areLessPlayerThanRequired){ + let removingPlayer = currentTeam.players[removingPlayerId]; + this.sendMessage( + TeamsConst.CHAT.MESSAGE.NOT_ENOUGH_PLAYERS, + 'Team', + currentTeam.clients[removingPlayerId], + removingPlayer.player_id, + removingPlayer.state.room_id, + ); + } + return true; + }); + } + + inviteClanAcceptedEventListener() + { + this.events.on('reldens.afterPlayerJoinedClan', (props) => { + let playerJoining = sc.get(props, 'playerJoining', false); + if(!playerJoining){ + return false; + } + let clan = sc.get(props, 'clan', false); + if(!clan){ + return false; + } + let playerJoiningName = playerJoining.playerName; + let message = this.createMessage( + TeamsConst.CHAT.MESSAGE.INVITE_ACCEPTED, + playerJoiningName + ); + this.sendMessage(message, 'Clan', clan.ownerClient, clan.owner.player_id, playerJoining.state.room_id); + }); + } + + inviteClanRejectedEventListener() + { + this.events.on('reldens.clanJoinInviteRejected', (props) => { + let clientSendingInvite = sc.get(props, 'clientSendingInvite', false); + let playerRejectingName = sc.get(props, 'playerRejectingName', false); + let clanInvite = sc.get(props, 'clanInvite', false); + if(!clientSendingInvite || !playerRejectingName){ + return false; + } + let message = this.createMessage( + TeamsConst.CHAT.MESSAGE.INVITE_REJECTED, + playerRejectingName + ); + this.sendMessage( + message, + 'Clan', + clientSendingInvite, + clanInvite.owner.player_id, + clanInvite.owner.state.room_id + ); + }); + } + + clanMemberLeavingEventListener() + { + this.events.on('reldens.clanLeaveBeforeSendUpdate', (props) => { + let playerLeavingId = sc.get(props, 'playerId', false); + if(!playerLeavingId){ + Logger.error('Leaving player ID undefined on "clanLeaveBeforeSendUpdate" event.'); + return false; + } + let currentClan = sc.get(props, 'currentClan', false); + if(!currentClan){ + Logger.error('Clan undefined on "clanLeaveBeforeSendUpdate" event.'); + return false; + } + let disbandClan = sc.get(props, 'disbandClan', false); + let leavingPlayer = currentClan.players[playerLeavingId]; + let leavingClient = currentClan.clients[playerLeavingId]; + if(disbandClan){ + let message = this.createMessage( + TeamsConst.CHAT.MESSAGE.DISBANDED, + leavingPlayer.playerName, + 'clan' + ); + this.sendMessage( + message, + 'Clan', + leavingClient, + leavingPlayer.player_id, + leavingPlayer.state.room_id + ); + return true; + } + let message = this.createMessage( + props.singleRemoveId ? TeamsConst.CHAT.MESSAGE.REMOVED : TeamsConst.CHAT.MESSAGE.LEAVE, + leavingPlayer.playerName, + 'clan' + ); + this.sendMessage(message, 'Clan', leavingClient, leavingPlayer.player_id, leavingPlayer.state.room_id); + return true; + }); + } + + createMessage(baseChatMessage = '', playerName = '', groupName = '') + { + let message = baseChatMessage.replace('%playerName', playerName); + // @TODO - BETA - Split class in multiple actions, remove the "groupName" since it will limit the translations. + if(groupName){ + message = message.replace('%groupName', groupName); + } + return message; + } + + sendMessage(message, chatFrom, client, playerId, roomId) + { + let messageObject = { + act: ChatConst.CHAT_ACTION, + [ChatConst.CHAT_FROM]: chatFrom, + [ChatConst.CHAT_MESSAGE]: message, + [ChatConst.CHAT_TO]: ChatConst.CHAT_TYPE_SYSTEM + }; + if(!client){ + Logger.critical('Client undefined for message.', message, chatFrom, client); + return false; + } + client.send('*', messageObject); + if(!playerId){ + Logger.error('Undefined playerId for save chat message.', message); + return false; + } + if(!roomId){ + Logger.error('Undefined roomId for save chat message.', message); + return false; + } + this.chatPlugin.chatManager.saveMessage(message, playerId, roomId, ChatConst.CHAT_TEAMS).catch((err) => { + Logger.error('Save team chat message error.', message, playerId, roomId, err); + }); + } + +} + +module.exports.ChatMessageActions = ChatMessageActions; diff --git a/lib/teams/server/message-actions/clan-create.js b/lib/teams/server/message-actions/clan-create.js new file mode 100644 index 000000000..bfe45cb06 --- /dev/null +++ b/lib/teams/server/message-actions/clan-create.js @@ -0,0 +1,109 @@ +/** + * + * Reldens - ClanMessageActions + * + */ + +const { ClanFactory } = require('../clan-factory'); +const { TeamsConst } = require('../../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class ClanCreate +{ + + static async execute(client, data, room, playerSchema, teamsPlugin) + { + let clanName = sc.cleanMessage( + data[TeamsConst.ACTIONS.CLAN_NAME], + room.config.getWithoutLogs('server/clan/settings/nameLimit', TeamsConst.NAME_LIMIT) + ); + if(!clanName){ + Logger.info('The provided clan name is invalid.'); + return this.clanCreateSend(client, TeamsConst.VALIDATION.CREATE_ERROR, clanName); + } + let repository = teamsPlugin.dataServer.getEntity('clan'); + let exists = await repository.loadOneBy('name', clanName); + if(exists){ + Logger.info('Clan name "'+clanName+'" is already taken.'); + return this.clanCreateSend(client, TeamsConst.VALIDATION.NAME_EXISTS, clanName); + } + let firstLevel = await this.fetchInitialLevel(teamsPlugin); + if(!firstLevel){ + Logger.info('Clan creation invalid level "'+clanName+'".'); + return this.clanCreateSend(client, TeamsConst.VALIDATION.LEVEL_ISSUE, clanName); + } + let playerId = playerSchema.player_id; + let createdClanModel = await repository.create({ + name: clanName, + owner_id: playerId, + points: room.config.getWithoutLogs('server/clan/settings/startingPoints', TeamsConst.CLAN_STARTING_POINTS), + level: firstLevel.key + }); + if(!createdClanModel){ + Logger.info('Clan creation error "'+clanName+'".', createdClanModel); + return this.clanCreateSend(client, TeamsConst.VALIDATION.CREATE_ERROR, clanName); + } + let clanId = createdClanModel.id; + let ownerMember = await teamsPlugin.dataServer.getEntity('clanMembers').create({ + clan_id: clanId, + player_id: playerId + }); + if(!ownerMember){ + Logger.info('Clan owner creation error "'+clanName+'".', createdClanModel, ownerMember); + return this.clanCreateSend(client, TeamsConst.VALIDATION.CREATE_OWNER_ERROR, clanName); + } + playerSchema.setPrivate('clan', clanId); + let createdClan = await ClanFactory.create( + clanId, + playerSchema, + client, + room.config.get('client/ui/teams/sharedProperties'), + teamsPlugin + ); + if(!createdClan){ + Logger.error('Clan "'+clanName+'" could not be created.'); + return false; + } + Logger.info('New clan created "'+clanName+'" with ID "'+clanId+'", by player ID "'+playerId+'".'); + return this.clanCreateSend( + client, + TeamsConst.VALIDATION.SUCCESS, + clanName, + playerId, + clanId + ); + } + + static async fetchInitialLevel(teamsPlugin) + { + let levelsRepository = teamsPlugin.dataServer.getEntity('clanLevels'); + levelsRepository.limit = 1; + levelsRepository.sortBy = 'key'; + let firstLevel = await levelsRepository.loadOne({}); + levelsRepository.limit = 0; + levelsRepository.sortBy = false; + return firstLevel; + } + + static clanCreateSend(client, result, clanName, ownerId, clanId) + { + // @TODO - BETA - Include additional event before send. + let sendData = { + act: TeamsConst.ACTIONS.CLAN_CREATE, + listener: TeamsConst.CLAN_KEY, + clanName, + result + }; + if(ownerId){ + sendData.ownerId = ownerId; + } + if(clanId){ + sendData.id = clanId; + } + client.send('*', sendData); + return true; + } + +} + +module.exports.ClanCreate = ClanCreate; diff --git a/lib/teams/server/message-actions/clan-disconnect.js b/lib/teams/server/message-actions/clan-disconnect.js new file mode 100644 index 000000000..a97716b36 --- /dev/null +++ b/lib/teams/server/message-actions/clan-disconnect.js @@ -0,0 +1,74 @@ +/** + * + * Reldens - ClanDisconnect + * + */ + +const { ClanUpdatesHandler } = require('../clan-updates-handler'); +const { TeamsConst } = require('../../constants'); +const { Logger } = require('@reldens/utils'); + +class ClanDisconnect +{ + + static async execute(playerSchema, teamsPlugin) + { + // @TODO - BETA - Consider extend TeamLeave. + if(!playerSchema){ + Logger.info('Player already left, wont disconnect from clan.'); + return false; + } + if(playerSchema?.physicalBody?.isChangingScene){ + Logger.info('Player is changing scene, avoid disconnect update.'); + return false; + } + let clanId = playerSchema?.privateData?.clan; + if(!clanId){ + Logger.info( + 'Clan ID not found in current player for disconnection.', + playerSchema.player_id, + playerSchema.privateData + ); + return false; + } + let clan = teamsPlugin.clans[clanId]; + if(!clan){ + Logger.error('Player "'+playerSchema.player_id+'" current clan "'+clanId+'" not found for disconnection.'); + return false; + } + // @NOTE: the way this works is by making the clients leave the clan and then updating the remaining players. + let playerId = playerSchema.player_id; + let client = clan.clients[playerId]; + if(1 === client?.ref?.readyState){ + let sendUpdate = { + act: TeamsConst.ACTIONS.CLAN_LEFT, + id: clan.owner.player_id, + listener: TeamsConst.KEY + }; + await teamsPlugin.events.emit('reldens.clanDisconnectBeforeSendUpdate', { + playerId, + sendUpdate, + playerSchema, + teamsPlugin + }); + client.send('*', sendUpdate); + } + clan.disconnect(clan.players[playerId]); + let afterDisconnectKeys = Object.keys(clan.players); + if(0 === afterDisconnectKeys.length){ + Logger.info('Last player on clan disconnected.'); + delete teamsPlugin.clans[clanId]; + return true; + } + let event = {playerSchema, teamsPlugin, continueLeave: true}; + await teamsPlugin.events.emit('reldens.clanDisconnectAfterSendUpdate', event); + if(!event.continueLeave){ + Logger.info('Stopped event "clanDisconnectAfterSendUpdate".'); + return false; + } + return ClanUpdatesHandler.updateClanPlayers(clan); + } + +} + +module.exports.ClanDisconnect = ClanDisconnect; diff --git a/lib/teams/server/message-actions/clan-join.js b/lib/teams/server/message-actions/clan-join.js new file mode 100644 index 000000000..7be9180f5 --- /dev/null +++ b/lib/teams/server/message-actions/clan-join.js @@ -0,0 +1,63 @@ +/** + * + * Reldens - ClanJoin + * + */ + +const { ClanUpdatesHandler } = require('../clan-updates-handler'); +const { ClanLeave } = require('./clan-leave'); +const { Logger } = require('@reldens/utils'); + +class ClanJoin +{ + + static async execute(client, data, room, playerSchema, teamsPlugin) + { + let clanToJoin = teamsPlugin.clans[data.id]; + if(!clanToJoin?.pendingInvites[playerSchema.player_id]){ + Logger.error('Player trying to join a clan without invite.'); + return false; + } + let previousClanId = playerSchema.getPrivate('clan'); + if(previousClanId){ + await ClanLeave.execute(playerSchema, teamsPlugin); + } + let eventBeforeJoin = {clanToJoin, teamsPlugin, continueBeforeJoin: true}; + await teamsPlugin.events.emit('reldens.beforeClanJoin', eventBeforeJoin); + if(!eventBeforeJoin.continueBeforeJoin){ + return false; + } + let result = await teamsPlugin.dataServer.getEntity('clanMembers').create({ + player_id: playerSchema.player_id, + clan_id: clanToJoin.id + }); + if(!result){ + Logger.critical('Clan member could not be created.', playerSchema?.id, clanToJoin?.id); + return false; + } + let newMemberModel = await teamsPlugin.dataServer.getEntity('clanMembers').loadOneByWithRelations( + {player_id: playerSchema.player_id, clan_id: clanToJoin.id}, + ['parent_player'] + ); + clanToJoin.join( + playerSchema, + client, + // @TODO - BETA - Replace this query with a constructed member, use the exiting player model and the result. + newMemberModel + ); + playerSchema.setPrivate('clan', clanToJoin.id); + let eventBeforeJoinUpdate = {clanToJoin, teamsPlugin, continueBeforeJoinUpdate: true}; + await teamsPlugin.events.emit('reldens.beforeClanUpdatePlayers', eventBeforeJoinUpdate); + if(!eventBeforeJoinUpdate.continueBeforeJoinUpdate){ + return false; + } + let updatedClan = ClanUpdatesHandler.updateClanPlayers(clanToJoin); + if(updatedClan){ + await teamsPlugin.events.emit('reldens.afterPlayerJoinedClan', { playerJoining: playerSchema, clan: clanToJoin }); + } + return updatedClan; + } + +} + +module.exports.ClanJoin = ClanJoin; diff --git a/lib/teams/server/message-actions/clan-leave.js b/lib/teams/server/message-actions/clan-leave.js new file mode 100644 index 000000000..99d03bc55 --- /dev/null +++ b/lib/teams/server/message-actions/clan-leave.js @@ -0,0 +1,95 @@ +/** + * + * Reldens - ClanLeave + * + */ + +const { ClanUpdatesHandler } = require('../clan-updates-handler'); +const { TeamsConst } = require('../../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class ClanLeave +{ + + static async fromMessage(message, playerSchema, teamsPlugin) + { + await teamsPlugin.events.emit('reldens.clanLeave', {message, playerSchema, teamsPlugin}); + let clanId = playerSchema?.privateData?.clan; + if(!clanId){ + Logger.warning('Clan ID not found in current player.', playerSchema); + return false; + } + let currentClan = teamsPlugin.clans[clanId]; + let playerSchemaId = playerSchema.player_id.toString(); + if(!currentClan){ + Logger.error('Player "'+playerSchemaId+'" current clan "'+clanId+'" not found.'); + return false; + } + let isPlayerFromClan = message.id === playerSchema.getPrivate('clan'); + let isClanOwner = currentClan.owner.player_id.toString() === playerSchemaId; + if(TeamsConst.ACTIONS.CLAN_REMOVE === message.act && (!isPlayerFromClan || !isClanOwner)){ + Logger.error('Clan remove failed, player "'+playerSchema.playerName+'" not allowed.'); + return false; + } + let singleRemoveId = sc.get(message, 'remove', playerSchema.player_id); + return await this.execute(playerSchema, teamsPlugin, singleRemoveId); + } + + static async execute(playerSchema, teamsPlugin, singleRemoveId) + { + let clanId = playerSchema?.privateData?.clan; + if(!clanId){ + Logger.warning('Clan ID not found in current player.', playerSchema); + return false; + } + let currentClan = teamsPlugin.clans[clanId]; + let playerSchemaId = playerSchema.player_id.toString(); + if(!currentClan){ + Logger.error('Player "'+playerSchemaId+'" current clan "'+clanId+'" not found.'); + return false; + } + let clanOwnerPlayerId = currentClan.owner.player_id.toString(); + let disbandClan = playerSchemaId === clanOwnerPlayerId && !singleRemoveId; + let removeByKeys = singleRemoveId + ? [singleRemoveId] + : (disbandClan ? Object.keys(currentClan.players) : [playerSchemaId]); + for(let playerId of removeByKeys){ + let sendUpdate = { + act: TeamsConst.ACTIONS.CLAN_INITIALIZE, + id: clanOwnerPlayerId, + listener: TeamsConst.CLAN_KEY + }; + await teamsPlugin.events.emit('reldens.clanLeaveBeforeSendUpdate', { + playerId, + sendUpdate, + currentClan, + disbandClan, + singleRemoveId, + playerSchema, + teamsPlugin + }); + currentClan.clients[playerId].send('*', sendUpdate); + currentClan.leave(currentClan.players[playerId]); + await teamsPlugin.dataServer.getEntity('clanMembers').deleteBy({'player_id': playerId}); + } + if(0 === Object.keys(currentClan.members).length){ + let event = {singleRemoveId, playerSchema, teamsPlugin, continueDisband: true}; + await teamsPlugin.events.emit('reldens.beforeClanDisband', event); + if(!event.continueDisband){ + return false; + } + delete teamsPlugin.clans[clanId]; + await teamsPlugin.dataServer.getEntity('clan').deleteById(clanId); + return true; + } + let event = {singleRemoveId, playerSchema, teamsPlugin, continueLeave: true}; + await teamsPlugin.events.emit('reldens.clanLeaveAfterSendUpdate', event); + if(!event.continueLeave){ + return false; + } + return ClanUpdatesHandler.updateClanPlayers(currentClan); + } + +} + +module.exports.ClanLeave = ClanLeave; diff --git a/lib/teams/server/message-actions/team-join.js b/lib/teams/server/message-actions/team-join.js new file mode 100644 index 000000000..72250958a --- /dev/null +++ b/lib/teams/server/message-actions/team-join.js @@ -0,0 +1,75 @@ +/** + * + * Reldens - TeamJoin + * + */ + +const { Team } = require('../team'); +const { TeamUpdatesHandler } = require('../team-updates-handler'); +const { TeamLeave } = require('./team-leave'); +const { Logger } = require('@reldens/utils'); + +class TeamJoin +{ + + static async execute(client, data, room, playerSchema, teamsPlugin) + { + if(playerSchema.player_id === data.id){ + Logger.info('The player is trying to join a team with himself.', playerSchema.player_id, data); + return false; + } + if(playerSchema.currentTeam){ + await TeamLeave.execute(room, playerSchema, teamsPlugin); + } + let teamOwnerPlayer = room.playerByPlayerIdFromState(data.id); + if(!teamOwnerPlayer){ + Logger.error('Player team owner not found.', teamOwnerPlayer, data); + return false; + } + if(teamOwnerPlayer.currentTeam && teamOwnerPlayer.currentTeam !== data.id){ + Logger.info('Player was already in a team, leaving Team ID "'+teamOwnerPlayer.currentTeam+'"'); + await TeamLeave.execute(room, teamOwnerPlayer, teamsPlugin); + } + let teamOwnerClient = room.fetchActivePlayerById(data.id); + if(!teamOwnerClient){ + Logger.error('Team join, player owner client not found.', teamOwnerClient, data); + return false; + } + let teamProps = { + owner: teamOwnerPlayer, + ownerClient: teamOwnerClient.client, + sharedProperties: room.config.get('client/ui/teams/sharedProperties') + }; + let currentTeam = teamsPlugin.teams[teamOwnerPlayer.player_id]; + if(!currentTeam){ + let beforeCreateEvent = {teamProps, teamsPlugin, continueBeforeCreate: true}; + await teamsPlugin.events.emit('reldens.beforeTeamCreate', beforeCreateEvent); + if(!beforeCreateEvent.continueBeforeCreate){ + return false; + } + currentTeam = new Team(teamProps); + } + let eventBeforeJoin = {currentTeam, teamsPlugin, continueBeforeJoin: true}; + await teamsPlugin.events.emit('reldens.beforeTeamJoin', eventBeforeJoin); + if(!eventBeforeJoin.continueBeforeJoin){ + return false; + } + currentTeam.join(playerSchema, client); + teamOwnerPlayer.currentTeam = teamOwnerPlayer.player_id; + playerSchema.currentTeam = teamOwnerPlayer.player_id; + teamsPlugin.teams[teamOwnerPlayer.player_id] = currentTeam; + let eventBeforeJoinUpdate = {currentTeam, teamsPlugin, continueBeforeJoinUpdate: true}; + await teamsPlugin.events.emit('reldens.beforeTeamUpdatePlayers', eventBeforeJoinUpdate); + if(!eventBeforeJoinUpdate.continueBeforeJoinUpdate){ + return false; + } + let updateSuccess = TeamUpdatesHandler.updateTeamPlayers(currentTeam); + if(updateSuccess){ + await teamsPlugin.events.emit('reldens.afterPlayerJoinedTeam', {currentTeam, playerJoining: playerSchema}); + } + return updateSuccess; + } + +} + +module.exports.TeamJoin = TeamJoin; diff --git a/lib/teams/server/message-actions/team-leave.js b/lib/teams/server/message-actions/team-leave.js new file mode 100644 index 000000000..fa7b4df35 --- /dev/null +++ b/lib/teams/server/message-actions/team-leave.js @@ -0,0 +1,91 @@ +/** + * + * Reldens - TeamLeave + * + */ + +const { TeamUpdatesHandler } = require('../team-updates-handler'); +const { TeamsConst } = require('../../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class TeamLeave +{ + + static async fromMessage(data, room, playerSchema, teamsPlugin) + { + await teamsPlugin.events.emit('reldens.teamLeave', {data, room, playerSchema, teamsPlugin}); + let singleRemoveId = sc.get(data, 'remove', false); + if(TeamsConst.ACTIONS.TEAM_REMOVE === data.act){ + if(data.id !== playerSchema.player_id){ + Logger.error('Team remove failed, player "'+playerSchema.playerName+'" not allowed.'); + return false; + } + if(!singleRemoveId){ + Logger.error('Team remove failed, missing single remove ID.', data); + return false; + } + } + return await this.execute(room, playerSchema, teamsPlugin, singleRemoveId); + } + + static async execute(room, playerSchema, teamsPlugin, singleRemoveId) + { + let teamId = playerSchema.currentTeam; + if(!teamId){ + return false; + } + let currentTeam = teamsPlugin.teams[teamId]; + if(!currentTeam){ + Logger.error('Player "'+playerSchema.player_id+'" current team "'+teamId+'" not found.'); + playerSchema.currentTeam = false; + return false; + } + // @NOTE: the way this works is by making the clients leave the team and then updating the remaining players. + let playerIds = Object.keys(currentTeam.players); + let isOwnerDisbanding = playerSchema.player_id === teamId && !singleRemoveId; + let areLessPlayerThanRequired = 2 >= playerIds.length; + let removeByKeys = isOwnerDisbanding || areLessPlayerThanRequired ? playerIds : [singleRemoveId]; + for(let playerId of removeByKeys){ + if(1 === currentTeam.clients[playerId]?.ref?.readyState){ + let sendUpdate = { + act: TeamsConst.ACTIONS.TEAM_LEFT, + id: currentTeam.owner.player_id, + listener: TeamsConst.KEY + }; + await teamsPlugin.events.emit('reldens.teamLeaveBeforeSendUpdate', { + playerId, + sendUpdate, + currentTeam, + isOwnerDisbanding, + areLessPlayerThanRequired, + singleRemoveId, + room, + playerSchema, + teamsPlugin + }); + currentTeam.clients[playerId].send('*', sendUpdate); + } + let leavingPlayerName = currentTeam.players[playerId].playerName; + currentTeam.leave(currentTeam.players[playerId]); + await teamsPlugin.events.emit('reldens.afterTeamLeave', {currentTeam, leavingPlayerName}); + } + if(1 >= Object.keys(currentTeam.players).length){ + let event = {singleRemoveId, room, playerSchema, teamsPlugin, continueDisband: true}; + await teamsPlugin.events.emit('reldens.beforeTeamDisband', event); + if(!event.continueDisband){ + return false; + } + delete teamsPlugin.teams[teamId]; + return true; + } + let event = {singleRemoveId, room, playerSchema, teamsPlugin, continueLeave: true}; + await teamsPlugin.events.emit('reldens.beforeTeamDisband', event); + if(!event.continueLeave){ + return false; + } + return TeamUpdatesHandler.updateTeamPlayers(currentTeam); + } + +} + +module.exports.TeamLeave = TeamLeave; diff --git a/lib/teams/server/message-actions/try-clan-invite.js b/lib/teams/server/message-actions/try-clan-invite.js new file mode 100644 index 000000000..635da62aa --- /dev/null +++ b/lib/teams/server/message-actions/try-clan-invite.js @@ -0,0 +1,63 @@ +/** + * + * Reldens - TryClanInvite + * + */ + +const { TeamsConst } = require('../../constants'); +const { Logger } = require('@reldens/utils'); + +class TryClanInvite +{ + + static async execute(client, data, room, playerSchema, teamsPlugin) + { + // @TODO - BETA - Replace send data by short constants so we will know like "data.id" here is the target ID. + if(playerSchema.player_id === data.id){ + Logger.info('The player is trying to clan up with himself.', playerSchema.player_id, data); + return false; + } + let clanId = playerSchema.getPrivate('clan'); + if(!clanId){ + Logger.error('Player without a clan is trying to send a clan invite.', playerSchema.player_id, data); + return false; + } + let clan = teamsPlugin.clans[clanId]; + if(!clan){ + Logger.critical( + 'Player has a clan ID but clan entity does not exists on TeamsPlugin.', + playerSchema.sessionId, + playerSchema.player_id, + data, + {availableClans: Object.keys(teamsPlugin.clans)} + ); + return false; + } + let toPlayer = room.fetchActivePlayerById(data.id); + if(false === toPlayer){ + Logger.error('Clan invite player not found.', toPlayer, data); + return false; + } + if(clan.playerBySessionId(data.id)){ + Logger.info('Player already exists in clan.'); + return false; + } + let sendData = { + act: TeamsConst.ACTIONS.CLAN_INVITE, + listener: TeamsConst.CLAN_KEY, + from: playerSchema.playerName, + id: clan.id, + ownerId: playerSchema.player_id + }; + let event = {client, data, room, playerSchema, teamsPlugin, continueStart: true}; + await teamsPlugin.events.emit('reldens.tryClanStart', event); + if(!event.continueStart){ + return false; + } + toPlayer.client.send('*', sendData); + clan.pendingInvites[data.id] = true; + return true; + } +} + +module.exports.TryClanInvite = TryClanInvite; diff --git a/lib/teams/server/message-actions/try-team-start.js b/lib/teams/server/message-actions/try-team-start.js new file mode 100644 index 000000000..1f565e670 --- /dev/null +++ b/lib/teams/server/message-actions/try-team-start.js @@ -0,0 +1,40 @@ +/** + * + * Reldens - TryTeamStart + * + */ + +const { TeamsConst } = require('../../constants'); +const { Logger } = require('@reldens/utils'); + +class TryTeamStart +{ + + static async execute(client, data, room, playerSchema, teamsPlugin) + { + if(playerSchema.player_id === data.id){ + Logger.info('The player is trying to team up with himself.', playerSchema.player_id, data); + return false; + } + let toPlayer = room.fetchActivePlayerById(data.id); + if(false === toPlayer){ + Logger.error('Team invite player not found.', toPlayer, data); + return false; + } + let sendData = { + act: TeamsConst.ACTIONS.TEAM_INVITE, + listener: TeamsConst.KEY, + from: playerSchema.playerName, + id: playerSchema.player_id, + }; + let event = {client, data, room, playerSchema, teamsPlugin, continueStart: true}; + await teamsPlugin.events.emit('reldens.tryTeamStart', event); + if(!event.continueStart){ + return false; + } + toPlayer.client.send('*', sendData); + return true; + } +} + +module.exports.TryTeamStart = TryTeamStart; diff --git a/lib/teams/server/models/objection-js/clan-levels-model.js b/lib/teams/server/models/objection-js/clan-levels-model.js new file mode 100644 index 000000000..1db40d121 --- /dev/null +++ b/lib/teams/server/models/objection-js/clan-levels-model.js @@ -0,0 +1,34 @@ +/** + * + * Reldens - ClanLevelsModel + * + */ + +const { ObjectionJsRawModel } = require('@reldens/storage'); + +class ClanLevelsModel extends ObjectionJsRawModel +{ + + static get tableName() + { + return 'clan_levels'; + } + + static get relationMappings() + { + let { ClanLevelsModifiersModel } = require('./clan-levels-modifiers-model'); + return { + modifiers: { + relation: this.HasManyRelation, + modelClass: ClanLevelsModifiersModel, + join: { + from: this.tableName+'.id', + to: ClanLevelsModifiersModel.tableName+'.level_id' + } + } + } + } + +} + +module.exports.ClanLevelsModel = ClanLevelsModel; diff --git a/lib/teams/server/models/objection-js/clan-levels-modifiers-model.js b/lib/teams/server/models/objection-js/clan-levels-modifiers-model.js new file mode 100644 index 000000000..df49325a8 --- /dev/null +++ b/lib/teams/server/models/objection-js/clan-levels-modifiers-model.js @@ -0,0 +1,34 @@ +/** + * + * 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'); + return { + parent_level: { + relation: this.HasOneRelation, + modelClass: ClanLevelsModel, + join: { + from: this.tableName+'.level_id', + to: ClanLevelsModel.tableName+'.id' + } + } + } + } + +} + +module.exports.ClanLevelsModifiersModel = ClanLevelsModifiersModel; diff --git a/lib/teams/server/models/objection-js/clan-members-model.js b/lib/teams/server/models/objection-js/clan-members-model.js new file mode 100644 index 000000000..32733d4b5 --- /dev/null +++ b/lib/teams/server/models/objection-js/clan-members-model.js @@ -0,0 +1,43 @@ +/** + * + * Reldens - ClanMembersModel + * + */ + +const { ObjectionJsRawModel } = require('@reldens/storage'); + +class ClanMembersModel extends ObjectionJsRawModel +{ + + static get tableName() + { + return 'clan_members'; + } + + static get relationMappings() + { + let { ClanModel } = require('./clan-model'); + let { PlayersModel } = require('../../../../users/server/models/objection-js/players-model'); + return { + parent_clan: { + relation: this.HasOneRelation, + modelClass: ClanModel, + join: { + from: this.tableName+'.clan_id', + to: ClanModel.tableName+'.id' + } + }, + parent_player: { + relation: this.HasOneRelation, + modelClass: PlayersModel, + join: { + from: this.tableName+'.player_id', + to: PlayersModel.tableName+'.id' + } + } + } + } + +} + +module.exports.ClanMembersModel = ClanMembersModel; diff --git a/lib/teams/server/models/objection-js/clan-model.js b/lib/teams/server/models/objection-js/clan-model.js new file mode 100644 index 000000000..9a41cb532 --- /dev/null +++ b/lib/teams/server/models/objection-js/clan-model.js @@ -0,0 +1,52 @@ +/** + * + * Reldens - ClanModel + * + */ + +const { ObjectionJsRawModel } = require('@reldens/storage'); + +class ClanModel extends ObjectionJsRawModel +{ + + static get tableName() + { + return 'clan'; + } + + static get relationMappings() + { + let { PlayersModel } = require('../../../../users/server/models/objection-js/players-model'); + let { ClanLevelsModel } = require('./clan-levels-model'); + let { ClanMembersModel } = require('./clan-members-model'); + return { + player_owner: { + relation: this.HasOneRelation, + modelClass: PlayersModel, + join: { + from: this.tableName+'.owner_id', + to: PlayersModel.tableName+'.id' + } + }, + parent_level: { + relation: this.HasOneRelation, + modelClass: ClanLevelsModel, + join: { + from: this.tableName+'.level', + to: ClanLevelsModel.tableName+'.key' + } + }, + members: { + relation: this.HasManyRelation, + modelClass: ClanMembersModel, + join: { + from: this.tableName+'.id', + to: ClanMembersModel.tableName+'.clan_id' + } + } + } + } + +} + +module.exports.ClanModel = ClanModel; diff --git a/lib/teams/server/models/objection-js/registered-models-objection-js.js b/lib/teams/server/models/objection-js/registered-models-objection-js.js new file mode 100644 index 000000000..ec15b1b65 --- /dev/null +++ b/lib/teams/server/models/objection-js/registered-models-objection-js.js @@ -0,0 +1,26 @@ +/** + * + * Reldens - Registered Models + * + */ + + +const { ClanModel } = require('../../models/objection-js/clan-model'); +const { ClanLevelsModel } = require('../../models/objection-js/clan-levels-model'); +const { ClanLevelsModifiersModel } = require('../../models/objection-js/clan-levels-modifiers-model'); +const { ClanMembersModel } = require('../../models/objection-js/clan-members-model'); +const { entitiesConfig } = require('../../entities-config'); +const { entitiesTranslations } = require('../../entities-translations'); + +let rawRegisteredEntities = { + clan: ClanModel, + clanLevels: ClanLevelsModel, + clanLevelsModifiers: ClanLevelsModifiersModel, + clanMembers: ClanMembersModel +}; + +module.exports.rawRegisteredEntities = rawRegisteredEntities; + +module.exports.entitiesConfig = entitiesConfig; + +module.exports.entitiesTranslations = entitiesTranslations; diff --git a/lib/teams/server/players-data-mapper.js b/lib/teams/server/players-data-mapper.js new file mode 100644 index 000000000..6b42e4882 --- /dev/null +++ b/lib/teams/server/players-data-mapper.js @@ -0,0 +1,46 @@ +/** + * + * Reldens - PlayersDataMapper + * + */ + +const { sc } = require('@reldens/utils'); + +class PlayersDataMapper +{ + + static fetchPlayersData(team) + { + let teamPlayersId = Object.keys(team.players); + let playersData = {}; + for(let i of teamPlayersId){ + playersData[i] = this.fetchPlayerData(team.players[i], team.sharedProperties); + } + return playersData; + } + + static fetchPlayerData(playerSchema, sharedProperties) + { + let playerData = { + name: playerSchema.playerName, + player_id: playerSchema.player_id, + sessionId: playerSchema.sessionId, + sharedProperties: {} + }; + for(let i of Object.keys(sharedProperties)){ + let propertyData = sharedProperties[i]; + playerData.sharedProperties[i] = { + label: propertyData.label, + value: sc.getByPath(playerSchema, propertyData.path.split('/'), 0), + }; + let pathMax = sc.get(propertyData, 'pathMax', ''); + if('' !== pathMax){ + playerData.sharedProperties[i].max = sc.getByPath(playerSchema, pathMax.split('/'), 0); + } + } + return playerData; + } + +} + +module.exports.PlayersDataMapper = PlayersDataMapper; diff --git a/lib/teams/server/plugin.js b/lib/teams/server/plugin.js new file mode 100644 index 000000000..7f8fa4e27 --- /dev/null +++ b/lib/teams/server/plugin.js @@ -0,0 +1,64 @@ +/** + * + * Reldens - Teams Server Plugin. + * + */ + +const { PluginInterface } = require('../../features/plugin-interface'); +const { ClanMessageActions } = require('./clan-message-actions'); +const { TeamMessageActions } = require('./team-message-actions'); +const { CreatePlayerClanHandler } = require('./event-handlers/create-player-clan-handler'); +const { CreatePlayerTeamHandler } = require('./event-handlers/create-player-team-handler'); +const { StatsUpdateHandler } = require('./event-handlers/stats-update-handler'); +const { EndPlayerHitChangePointTeamHandler } = require('./event-handlers/end-player-hit-change-point-team-handler'); +const { TeamLeave } = require('./message-actions/team-leave'); +const { ClanDisconnect } = require('./message-actions/clan-disconnect'); +const { ChatMessageActions } = require('./message-actions/chat-message-actions'); +const { Logger, sc } = require('@reldens/utils'); + +class TeamsPlugin extends PluginInterface +{ + + setup(props) + { + this.events = sc.get(props, 'events', false); + if(!this.events){ + Logger.error('EventsManager undefined in TeamsPlugin.'); + } + this.dataServer = sc.get(props, 'dataServer', false); + if(!this.dataServer){ + Logger.error('DataServer undefined in TeamsPlugin.'); + } + this.teams = sc.get(props, 'teams', {}); + this.clans = sc.get(props, 'clans', {}); + this.teamChangingRoomPlayers = sc.get(props, 'teamChangingRoomPlayers', {}); + this.clanChangingRoomPlayers = sc.get(props, 'clanChangingRoomPlayers', {}); + this.events.on('reldens.roomsMessageActionsGlobal', (roomMessageActions) => { + roomMessageActions.teams = new TeamMessageActions({teamsPlugin: this}); + roomMessageActions.clan = new ClanMessageActions({teamsPlugin: this}); + }); + this.events.on('reldens.createPlayerAfter', async (client, authResult, playerSchema, room) => { + await CreatePlayerClanHandler.enrichPlayerWithClan(client, playerSchema, room, this); + await CreatePlayerTeamHandler.joinExistentTeam(client, playerSchema, this); + }); + this.events.on('reldens.savePlayerStatsUpdateClient', async (client, playerSchema) => { + await StatsUpdateHandler.updateTeam({teamsPlugin: this, playerSchema}); + await StatsUpdateHandler.updateClan({teamsPlugin: this, playerSchema}); + }); + this.events.on('reldens.endPlayerHitChangePoint', async (event) => { + await EndPlayerHitChangePointTeamHandler.savePlayerTeam(event.playerSchema, this); + }); + this.events.on('reldens.saveStateAndRemovePlayerBefore', async(event) => { + await TeamLeave.execute(event.room, event.playerSchema, this); + await ClanDisconnect.execute(event.playerSchema, this); + }); + this.chatPlugin = props.featuresManager.featuresList?.chat?.package; + if(this.chatPlugin){ + let chatMessageActions = new ChatMessageActions({events: this.events, chatPlugin: this.chatPlugin}); + chatMessageActions.listenEvents(); + } + } + +} + +module.exports.TeamsPlugin = TeamsPlugin; diff --git a/lib/teams/server/team-message-actions.js b/lib/teams/server/team-message-actions.js new file mode 100644 index 000000000..954148366 --- /dev/null +++ b/lib/teams/server/team-message-actions.js @@ -0,0 +1,61 @@ +/** + * + * Reldens - TeamMessageActions + * + */ + +const { TryTeamStart } = require('./message-actions/try-team-start'); +const { TeamJoin } = require('./message-actions/team-join'); +const { TeamLeave } = require('./message-actions/team-leave'); +const { TeamsConst } = require('../constants'); +const { Logger, sc } = require('@reldens/utils'); + +class TeamMessageActions +{ + + constructor(props) + { + this.teamsPlugin = props.teamsPlugin; + } + + async executeMessageActions(client, data, room, playerSchema) + { + if(!sc.hasOwn(data, 'act')){ + return false; + } + if(0 !== data.act.indexOf(TeamsConst.TEAM_PREF)){ + return false; + } + if(TeamsConst.ACTIONS.TEAM_INVITE === data.act){ + return await TryTeamStart.execute(client, data, room, playerSchema, this.teamsPlugin); + } + if(TeamsConst.ACTIONS.TEAM_ACCEPTED === data.act && '1' === data.value){ + return await TeamJoin.execute(client, data, room, playerSchema, this.teamsPlugin); + } + if(TeamsConst.ACTIONS.TEAM_ACCEPTED === data.act && '2' === data.value){ + let activePlayerSendingInvite = room.fetchActivePlayerById(data.id); + if(false === activePlayerSendingInvite){ + Logger.error( + 'Team invitation declined, player owner client not found.', + Object.keys(room.activePlayers), + data + ); + return false; + } + let playerRejectingName = playerSchema.playerName; + await this.teamsPlugin.events.emit('reldens.teamJoinInviteRejected', { + activePlayerSendingInvite, + playerRejectingName + }); + } + if(TeamsConst.ACTIONS.TEAM_LEAVE === data.act){ + return await TeamLeave.fromMessage(data, room, playerSchema, this.teamsPlugin); + } + if(TeamsConst.ACTIONS.TEAM_REMOVE === data.act){ + return await TeamLeave.fromMessage(data, room, playerSchema, this.teamsPlugin); + } + } + +} + +module.exports.TeamMessageActions = TeamMessageActions; diff --git a/lib/teams/server/team-updates-handler.js b/lib/teams/server/team-updates-handler.js new file mode 100644 index 000000000..8d06917e1 --- /dev/null +++ b/lib/teams/server/team-updates-handler.js @@ -0,0 +1,55 @@ +/** + * + * Reldens - TeamUpdatesHandler + * + */ + +const { PlayersDataMapper } = require('./players-data-mapper'); +const { TeamsConst } = require('../constants'); +const { Logger } = require('@reldens/utils'); + +class TeamUpdatesHandler +{ + + static updateTeamPlayers(team) + { + let clientsKeys = Object.keys(team.clients); + if(0 === clientsKeys.length){ + return false; + } + let playersList = PlayersDataMapper.fetchPlayersData(team); + if(0 === Object.keys(playersList).length){ + Logger.info('Team update without players.', team); + return false; + } + for(let i of clientsKeys){ + let otherPlayersData = Object.assign({}, playersList); + delete otherPlayersData[i]; + let sendUpdate = Object.assign( + {}, + { + act: this.actionConstant(), + id: team.owner.player_id, + listener: this.listenerKey(), + players: otherPlayersData, + leaderName: team.owner.playerName, + } + ); + team.clients[i].send('*', sendUpdate); + } + return true; + } + + static listenerKey() + { + return TeamsConst.KEY; + } + + static actionConstant() + { + return TeamsConst.ACTIONS.TEAM_UPDATE; + } + +} + +module.exports.TeamUpdatesHandler = TeamUpdatesHandler; diff --git a/lib/teams/server/team.js b/lib/teams/server/team.js new file mode 100644 index 000000000..22c2f2832 --- /dev/null +++ b/lib/teams/server/team.js @@ -0,0 +1,84 @@ +/** + * + * Reldens - Team + * + */ + +const { ModifierConst } = require('@reldens/modifiers'); +const { ErrorManager, sc } = require('@reldens/utils'); + +class Team +{ + + constructor(props) + { + this.level = sc.get(props, 'level', 1); + this.modifiers = sc.get(props, 'modifiers', {}); + this.sharedProperties = sc.get(props, 'sharedProperties', {}); + this.owner = sc.get(props, 'owner', false); + if(false === this.owner){ + ErrorManager.error('Team owner undefined.', props); + } + this.ownerClient = sc.get(props, 'ownerClient', false); + if(false === this.ownerClient){ + ErrorManager.error('Team owner client undefined.', props); + } + let players = {}; + let clients = {}; + players[this.owner.player_id] = this.owner; + clients[this.owner.player_id] = this.ownerClient; + this.players = sc.get(props, 'players', players); + this.clients = sc.get(props, 'clients', clients); + } + + join(playerSchema, client) + { + this.players[playerSchema.player_id] = playerSchema; + this.clients[playerSchema.player_id] = client; + return this.applyModifiers(playerSchema); + } + + leave(playerSchema) + { + this.revertModifiers(playerSchema); + playerSchema.currentTeam = false; + delete this.clients[playerSchema.player_id]; + delete this.players[playerSchema.player_id]; + return true; + } + + applyModifiers(playerSchema) + { + let modifiersKeys = Object.keys(this.modifiers); + if(0 === modifiersKeys.length){ + return true; + } + for(let i of modifiersKeys){ + let modifier = this.modifiers[i]; + modifier.apply(playerSchema); + if(ModifierConst.MOD_APPLIED !== modifier.state){ + return false; + } + } + return true; + } + + revertModifiers(playerSchema) + { + let modifiersKeys = Object.keys(this.modifiers); + if(0 === modifiersKeys.length){ + return true; + } + for(let i of modifiersKeys){ + let modifier = this.modifiers[i]; + modifier.revert(playerSchema); + if(ModifierConst.MOD_REVERTED !== modifier.state){ + return false; + } + } + return true; + } + +} + +module.exports.Team = Team; \ No newline at end of file diff --git a/lib/users/client/lifebar-ui.js b/lib/users/client/lifebar-ui.js index d942ddc50..33a27f7eb 100644 --- a/lib/users/client/lifebar-ui.js +++ b/lib/users/client/lifebar-ui.js @@ -296,12 +296,12 @@ class LifebarUi getCurrentTargetId() { - return sc.get(this.gameManager.getCurrentPlayer().currentTarget, 'id', false); + return sc.get(this.gameManager.getCurrentPlayer()?.currentTarget, 'id', false); } getObjectByKey(objectKey) { - return sc.get(this.gameManager.getActiveScene().objectsAnimations, objectKey, false); + return sc.get(this.gameManager.getActiveScene()?.objectsAnimations, objectKey, false); } } diff --git a/lib/users/client/player-engine.js b/lib/users/client/player-engine.js index 4ae52b03d..e861da248 100644 --- a/lib/users/client/player-engine.js +++ b/lib/users/client/player-engine.js @@ -2,20 +2,19 @@ * * Reldens - PlayerEngine * - * PlayerEngine is the class that handle the player actions in the client side. - * */ const { SpriteTextFactory } = require('../../game/client/engine/sprite-text-factory'); -const { Logger, sc } = require('@reldens/utils'); const { GameConst } = require('../../game/constants'); const { ActionsConst } = require('../../actions/constants'); +const { Logger, sc } = require('@reldens/utils'); class PlayerEngine { constructor(props) { + // @TODO - BETA - Remove this PlayerEngine class and extend the same model Player from server and client. let {scene, playerData, gameManager, room} = props; this.scene = scene; this.config = gameManager.config; @@ -27,12 +26,14 @@ class PlayerEngine this.state = playerData.state; this.room = room; this.playerId = room.sessionId; + this.player_id = playerData.player_id; // id from storage this.players = {}; this.playedTime = playerData.playedTime; this.mov = false; this.dir = false; this.currentTarget = false; this.pointsValidator = false; + // @TODO - BETA - Set all the configs in a single config property. this.animationBasedOnPress = this.config.get('client/players/animations/basedOnPress'); this.topOff = this.gameManager.config.get('client/players/size/topOffset'); this.leftOff = this.gameManager.config.get('client/players/size/leftOffset'); @@ -52,7 +53,8 @@ class PlayerEngine dir: this.state.dir, playerName: this.playerName, avatarKey: this.avatarKey, - playedTime: this.playedTime + playedTime: this.playedTime, + player_id: this.player_id }; this.addPlayer(this.playerId, addPlayerData); this.scene.cameras.main.fadeFrom(this.fadeDuration); @@ -64,17 +66,18 @@ class PlayerEngine addPlayer(id, addPlayerData) { - // @TODO - BETA - Create a PlayersManager attached to the Scene and move all the CRUD methods into it. + // @TODO - BETA - Create a PlayersManager attached to the Scene and move all the players handler methods there. if(sc.hasOwn(this.players, id)){ // player sprite already exists, update it and return it: return this.players[id]; } - let {x, y, dir, playerName, avatarKey, playedTime} = addPlayerData; + let {x, y, dir, playerName, avatarKey, playedTime, player_id} = addPlayerData; this.players[id] = this.scene.physics.add.sprite(x, (y - this.topOff), avatarKey); this.players[id].playerName = playerName; this.players[id].playedTime = playedTime; this.players[id].avatarKey = avatarKey; this.players[id].playerId = id; + this.players[id].player_id = player_id; this.players[id].anims.play(avatarKey+'_'+dir); this.players[id].anims.stop(); this.showPlayerName(id); @@ -91,18 +94,28 @@ class PlayerEngine this.players[id].setInteractive({useHandCursor: true}).on('pointerdown', (e) => { // @NOTE: we avoid execute object interactions while the UI element is open, if we click on the UI the other // elements in the background scene should not be executed. - if('CANVAS' !== e.downElement.nodeName){ + if(GameConst.CANVAS !== e.downElement.nodeName){ return false; } // @NOTE: we could send a specific action when the player has been targeted. // this.room.send('*', {act: GameConst.TYPE_PLAYER, id: id}); // update target ui: - let previousTarget = Object.assign({}, this.currentTarget); - this.currentTarget = {id: id, type: GameConst.TYPE_PLAYER}; - this.gameManager.gameEngine.showTarget(this.players[id].playerName, this.currentTarget, previousTarget); + this.setTargetPlayerById(id); }); } + setTargetPlayerById(id) + { + if(!sc.get(this.players, id, false)){ + Logger.info('Target player ID "'+id+'" was not found.'); + this.gameManager.gameEngine.clearTarget(); + return false; + } + let previousTarget = Object.assign({}, this.currentTarget); + this.currentTarget = {id: id, type: GameConst.TYPE_PLAYER, player_id: this.players[id].player_id}; + this.gameManager.gameEngine.showTarget(this.players[id].playerName, this.currentTarget, previousTarget); + } + showPlayerName(id) { if(!this.globalConfigShowNames){ @@ -121,8 +134,9 @@ class PlayerEngine updatePlayer(playerId, player) { let playerSprite = this.players[playerId]; - if(!playerSprite.anims){ - Logger.error('PlayerSprite animation not defined.'); + if(!playerSprite){ + Logger.error('PlayerSprite not defined.', this.players, playerId); + return; } if(this.scene.clientInterpolation){ this.scene.interpolatePlayersPosition[playerId] = player.state; diff --git a/lib/users/client/player-stats-ui.js b/lib/users/client/player-stats-ui.js index f117cd8a9..7f07454d1 100644 --- a/lib/users/client/player-stats-ui.js +++ b/lib/users/client/player-stats-ui.js @@ -22,6 +22,7 @@ class PlayerStatsUi scenePreloader.load.html('playerStat', 'assets/html/player-stat.html'); }); this.events.on('reldens.beforeCreateUiScene', (scenePreloader) => { + // @TODO - BETA - Replace by UserInterface. let statsUi = scenePreloader.getUiConfig('playerStats'); if(!statsUi.enabled){ return false; diff --git a/lib/users/server/manager.js b/lib/users/server/manager.js index 8dbe2c88f..894a76cd4 100644 --- a/lib/users/server/manager.js +++ b/lib/users/server/manager.js @@ -21,6 +21,15 @@ class UsersManager } } + async fetchUserByNameOrEmail(username, email) + { + let result = await this.loadUserByUsername(username); + if(result){ + return result; + } + return await this.loadUserByEmail(email); + } + async loadUserByUsername(username) { let result = false; @@ -41,8 +50,11 @@ class UsersManager if(!email){ ErrorManager.error('Missing email.'); } - let loadedUser = await this.dataServer.getEntity('users') - .loadOneByWithRelations('email', email, ['players.[state]']); + let loadedUser = await this.dataServer.getEntity('users').loadOneByWithRelations( + 'email', + email, + ['players.[state]'] + ); if(loadedUser){ result = loadedUser; } @@ -56,7 +68,7 @@ class UsersManager async isNameAvailable(playerName) { - return !!(await this.dataServer.getEntity('players').loadOneBy('name', playerName)); + return await this.dataServer.getEntity('players').loadOneBy('name', playerName)?.name; } async createPlayer(playerData) diff --git a/lib/users/server/player.js b/lib/users/server/player.js index ac0edf33f..28ee55848 100644 --- a/lib/users/server/player.js +++ b/lib/users/server/player.js @@ -2,15 +2,11 @@ * * Reldens - Player * - * Player schema, this class get the player data and keep the state in sync. - * */ -const schema = require('@colyseus/schema'); -const { Schema, type } = schema; +const { Schema, type, defineTypes } = require('@colyseus/schema'); const { BodyState } = require('../../world/server/body-state'); const { GameConst } = require('../../game/constants'); -const { ErrorManager } = require('@reldens/utils'); class Player extends Schema { @@ -18,30 +14,53 @@ class Player extends Schema constructor(data, sessionId) { super(); - try { - let player = data.player; - // player data: - this.id = data.id; // this is the user id - this.player_id = player.id; - this.sessionId = sessionId; - this.broadcastKey = sessionId; - this.role_id = data.role_id; - this.status = data.status; - this.username = data.username; - this.playerName = player.name; - this.physicalBody = false; - this.eventsPrefix = 'p'+player.id+'.'+this.sessionId; - // body state contains the scene and position data: - this.state = new BodyState(player.state); - // stats for now will use the stats model. - this.stats = player.stats; // this is the current value - this.statsBase = player.statsBase; // this is the base or max value - this.avatarKey = GameConst.IMAGE_PLAYER; - this.inState = GameConst.STATUS.ACTIVE; - this.playedTime = data.played_time; - } catch (err) { - ErrorManager.error(['Player creation missing data.', err]); - } + let player = data.player; + // @TODO - BETA - Replace "id" to be "player_id" and set the ID from the model as userId. + this.userId = data.id.toString(); // this is the user_id from the storage + // @TODO - BETA - Use camelCase on player_id and role_id. + this.player_id = player.id.toString(); // this is the player_id from the storage + this.sessionId = sessionId; + this.broadcastKey = sessionId; + this.role_id = data.role_id; + this.status = data.status; + this.username = data.username; + this.playerName = player.name; + this.physicalBody = false; + this.eventsPrefix = 'p'+player.id+'.'+this.sessionId; + // body state contains the scene and position data: + this.state = new BodyState(player.state); + // @NOTE: stats can't be a schema because is private data manually handled. + this.stats = player.stats; // this is the current value + this.statsBase = player.statsBase; // this is the base or max value + this.avatarKey = GameConst.IMAGE_PLAYER; + this.inState = GameConst.STATUS.ACTIVE; + this.playedTime = data.played_time; + // @TODO - BETA - Look for references where we assign data on the player using dynamic properties and move + // the data inside this privateData property. For example: playerSchema.currentAction, actions, inventory, + // skillsServer, physicalBody, etc. should become playerSchema.privateData.currentAction. + this.privateData = {}; + this.customData = {}; + } + + // @TODO - BETA - Enclose a single entity that would contain the model, the schema, the client, the extra data, etc. + getPrivate(key) + { + return this.privateData[key] || null; + } + + setPrivate(key, data) + { + return this.privateData[key] = data; + } + + getCustom(key) + { + return this.customData[key] || null; + } + + setCustom(key, data) + { + return this.customData[key] = data; } getPosition() @@ -55,12 +74,13 @@ class Player extends Schema } type('string')(Player.prototype, 'sessionId'); +type('string')(Player.prototype, 'player_id'); type('string')(Player.prototype, 'username'); type('string')(Player.prototype, 'playerName'); type('number')(Player.prototype, 'playedTime'); type('string')(Player.prototype, 'status'); type('string')(Player.prototype, 'avatarKey'); type('string')(Player.prototype, 'broadcastKey'); -schema.defineTypes(Player, {state: BodyState}); +defineTypes(Player, {state: BodyState}); module.exports.Player = Player; diff --git a/lib/users/server/plugin.js b/lib/users/server/plugin.js index 92d8e38cf..c052382d5 100644 --- a/lib/users/server/plugin.js +++ b/lib/users/server/plugin.js @@ -26,7 +26,7 @@ class UsersPlugin extends PluginInterface if(!this.dataServer){ Logger.error('DataServer undefined in UsersPlugin.'); } - // @TODO - BETA - Move LifeBar to it's own package. + // @TODO - BETA - Move LifeBar to it's own package, convert into PropertyUpdater and make generic. this.lifeBarConfig = false; this.lifeProp = false; this.events.on('reldens.serverReady', async (event) => { @@ -54,17 +54,18 @@ class UsersPlugin extends PluginInterface if(!this.lifeProp){ this.lifeProp = configProcessor.get('client/actions/skills/affectedProperty'); } - this.events.on('reldens.createPlayerAfter', async (client, authResult, currentPlayer, roomScene) => { - await this.updatePlayersLifebar(roomScene, client, currentPlayer); + this.events.on('reldens.createPlayerStatsAfter', async (client, authResult, playerSchema, roomScene) => { + await this.updatePlayersLifebar(roomScene, client, playerSchema); await this.updateEnemiesLifebar(roomScene); }); - this.events.on('reldens.savePlayerStatsUpdateClient', async (client, target, roomScene) => { - await this.onSavePlayerStatsUpdateClient(client, target, roomScene); + this.events.on('reldens.savePlayerStatsUpdateClient', async (client, playerSchema, roomScene) => { + await this.onSavePlayerStatsUpdateClient(client, playerSchema, roomScene); }); this.events.on('reldens.runBattlePveAfter', async (event) => { return this.sendLifeBarUpdate(event); }); this.events.on('reldens.actionsPrepareEventsListeners', async (actionsPack, classPath) => { + // @TODO - BETA - Make sure lifebar is updated on every stats change and not only after damage was applied. classPath.listenEvent(SkillsEvents.SKILL_ATTACK_APPLY_DAMAGE, async (skill, target) => { let client = skill.owner.skillsServer.client.client; if(sc.hasOwn(target, 'player_id')){ @@ -101,11 +102,12 @@ class UsersPlugin extends PluginInterface this.broadcastObjectUpdate(roomScene, target) } - async updatePlayersLifebar(roomScene, client, currentPlayer) + async updatePlayersLifebar(roomScene, client, playerSchema) { - this.lifeBarConfig.showAllPlayers || this.lifeBarConfig.showOnClick - ? await this.updateAllPlayersLifeBars(roomScene) - : await this.onSavePlayerStatsUpdateClient(client, currentPlayer, roomScene); + if(this.lifeBarConfig.showAllPlayers || this.lifeBarConfig.showOnClick){ + return await this.updateAllPlayersLifeBars(roomScene); + } + return await this.onSavePlayerStatsUpdateClient(client, playerSchema, roomScene); } async updateEnemiesLifebar(roomScene) @@ -135,7 +137,7 @@ class UsersPlugin extends PluginInterface return false; } for(let i of roomScene.playersKeysFromState()){ - let player = roomScene.playerByIdFromState(i); + let player = roomScene.playerBySessionIdFromState(i); let updateData = { act: UsersConst.ACTION_LIFEBAR_UPDATE, oT: ActionsConst.DATA_TYPE_VALUE_PLAYER, @@ -145,6 +147,7 @@ class UsersPlugin extends PluginInterface }; roomScene.broadcast('*', updateData); } + return true; } async preparePlayersStats(configProcessor) @@ -173,11 +176,12 @@ class UsersPlugin extends PluginInterface configProcessor.client.players.initialStats = this.statsByKey; } - async onCreatePlayerAfterAppendStats(client, authResult, currentPlayer) + async onCreatePlayerAfterAppendStats(client, authResult, currentPlayer, roomScene) { let {stats, statsBase} = await this.processStatsData('playerStats', currentPlayer.player_id); currentPlayer.stats = stats; currentPlayer.statsBase = statsBase; + this.events.emit('reldens.createPlayerStatsAfter', client, authResult, currentPlayer, roomScene); } async processStatsData(model, playerId) @@ -196,25 +200,29 @@ class UsersPlugin extends PluginInterface return {stats, statsBase}; } - async onSavePlayerStatsUpdateClient(client, target, roomScene) + async onSavePlayerStatsUpdateClient(client, playerSchema, roomScene) { if( - client.sessionId !== target.sessionId + client.sessionId !== playerSchema.sessionId && !this.lifeBarConfig.showAllPlayers && !this.lifeBarConfig.showOnClick ){ return false; } + // @TODO - BETA - Replace "oT", "oK" by constants. let updateData = { act: UsersConst.ACTION_LIFEBAR_UPDATE, oT: 'p', - oK: target.sessionId, - newValue: target.stats[this.lifeProp], - totalValue: target.statsBase[this.lifeProp] + oK: playerSchema.sessionId, + newValue: playerSchema.stats[this.lifeProp], + totalValue: playerSchema.statsBase[this.lifeProp] }; - this.lifeBarConfig.showAllPlayers || this.lifeBarConfig.showOnClick - ? roomScene.broadcast('*', updateData) - : client.send('*', updateData); + if(this.lifeBarConfig.showAllPlayers || this.lifeBarConfig.showOnClick){ + roomScene.broadcast('*', updateData); + return true; + } + client.send('*', updateData); + return true; } } diff --git a/lib/world/server/collisions-manager.js b/lib/world/server/collisions-manager.js index 47df37075..7624bf641 100644 --- a/lib/world/server/collisions-manager.js +++ b/lib/world/server/collisions-manager.js @@ -56,6 +56,25 @@ class CollisionsManager player.stopFull(); bullet.stopFull(); } + this.removeIdleBullets(); + } + + removeIdleBullets() + { + if(0 === this.room.roomWorld.removeBulletsStateIds.length){ + return; + } + let fixedStateIds = [...this.room.roomWorld.removeBulletsStateIds]; + for(let stateId of fixedStateIds){ + this.room.roomWorld.removeBulletsStateIds.splice( + this.room.roomWorld.removeBulletsStateIds.indexOf(stateId), + 1 + ); + if(!this.room.state.bodies[stateId]){ + continue; + } + delete this.room.state.bodies[stateId]; + } } beforeResolveCollision(evt) @@ -128,28 +147,33 @@ class CollisionsManager { // @NOTE: we could run specific events when a player collides with another player. // Logger.info(['Hit player!', bodyA.playerId, bodyB.playerId]); + this.room.events.emit('reldens.playerHitPlayer', {bodyA, bodyB}); } playerHitObject(playerBody, otherBody) { + this.room.events.emit('reldens.startPlayerHitObject', {playerBody, otherBody}); // if the player collides with something we need to restart the pathfinder if it was active: if(playerBody.autoMoving && 1 < playerBody.autoMoving.length){ let destPoint = playerBody.autoMoving.pop(); playerBody.moveToPoint({column: destPoint[0], row: destPoint[1]}); } // now the collision manager only run the object hit action: - if(otherBody.roomObject){ + if(otherBody.roomObject && sc.isFunction(otherBody.roomObject.onHit)){ otherBody.roomObject.onHit({bodyA: playerBody, bodyB: otherBody, room: this.room}); } + this.room.events.emit('reldens.endPlayerHitObject', {playerBody, otherBody}); } playerHitWallBegin(playerBody, wall) { // available to add specifics + this.room.events.emit('reldens.playerHitWallBegin', {playerBody, wall}); } playerHitWall(playerBody) { + this.room.events.emit('reldens.startPlayerHitWall', {playerBody}); // @NOTE: we can use wall.material to trigger an action over the player, like: // wall.material = lava > reduce player.hp in every step // if the player collides with something we need to restart the pathfinder if it was active: @@ -161,41 +185,50 @@ class CollisionsManager if(playerBody.world && !playerBody.world.applyGravity){ playerBody.stopFull(true); } + this.room.events.emit('reldens.endPlayerHitWall', {playerBody}); } playerHitObjectEnd(playerBody) { - playerBody.stopFull(true); + let result = {resultValue: true}; + this.room.events.emit('reldens.playerHitObjectEnd', {playerBody, result}); + playerBody.stopFull(result.resultValue); } playerHitChangePoint(playerBody, changePoint) { + this.room.events.emit('reldens.startPlayerHitChangePoint', {collisionsManager: this, playerBody, changePoint}); playerBody.resetAuto(); // check if the player is not changing scenes already: let isChangingScene = sc.get(playerBody, 'isChangingScene', false); - if(true === isChangingScene){ + if(isChangingScene){ // @NOTE: if the player is already changing scene do nothing. - Logger.error('Player is busy for a change point: ' + playerBody.playerId); + Logger.info('Player is busy for a change point: ' + playerBody.playerId); return false; } let playerPosition = {x: playerBody.position[0], y: playerBody.position[1]}; this.room.state.positionPlayer(playerBody.playerId, playerPosition); let playerSchema = this.room.getPlayerFromState(playerBody.playerId); - let changeScene = changePoint.changeScenePoint; - let previousScene = playerSchema.state.scene; - let changeData = {prev: previousScene, next: changeScene}; + let changeData = {prev: playerSchema.state.scene, next: changePoint.changeScenePoint}; playerBody.isChangingScene = true; let contactClient = this.room.getClientById(playerBody.playerId); // @NOTE: we do not need to change back the isChangingScene property back to false since in the new // scene a new body will be created with the value set to false by default. - this.room.nextSceneInitialPosition(contactClient, changeData).catch((err) => { - Logger.error('nextSceneInitialPosition error: '+err); + this.room.nextSceneInitialPosition(contactClient, changeData, playerBody).catch((err) => { + Logger.error('There was an error while setting the next scene initial position.', err); + }); + this.room.events.emit('reldens.endPlayerHitChangePoint', { + collisionsManager: this, + playerSchema, + playerBody, + changePoint, + changeData }); } objectHitObject(bodyA, bodyB) { - // @TODO - BETA - Fix bullet hit bullet. + this.room.events.emit('reldens.startObjectHitObject', {bodyA, bodyB}); let aPriority = sc.hasOwn(bodyA, 'hitPriority'); let bPriority = sc.hasOwn(bodyB, 'hitPriority'); let onHitData = {bodyA: bodyA, bodyB: bodyB, room: this.room}; @@ -205,27 +238,33 @@ class CollisionsManager if(!priorityObject.roomObject){ return; } - priorityObject.roomObject.onHit(onHitData); + if(sc.isFunction(priorityObject.roomObject?.onHit)){ + priorityObject.roomObject.onHit(onHitData); + } + if(bodyA.isBullet){ + bodyA.roomObject.removeBullet(bodyA); + } + if(bodyB.isBullet){ + bodyB.roomObject.removeBullet(bodyB); + } + this.room.events.emit('reldens.endObjectHitObject', {bodyA, bodyB, priorityObject}); } getWallBody(evt) { - let bodyA = evt.bodyA, - bodyB = evt.bodyB; + let {bodyA, bodyB} = evt; return bodyA && bodyA.isWall ? bodyA : (bodyB && bodyB.isWall ? bodyB : false); } getObjectBody(evt) { - let bodyA = evt.bodyA, - bodyB = evt.bodyB; + let {bodyA, bodyB} = evt; return bodyA && bodyA.isRoomObject ? bodyA : (bodyB && bodyB.isRoomObject ? bodyB : false); } getPlayerBody(evt) { - let bodyA = evt.bodyA, - bodyB = evt.bodyB; + let {bodyA, bodyB} = evt; return bodyA && bodyA.playerId ? bodyA : (bodyB && bodyB.playerId ? bodyB : false); } diff --git a/lib/world/server/p2world.js b/lib/world/server/p2world.js index 5cd4b3df7..3f81d84e0 100644 --- a/lib/world/server/p2world.js +++ b/lib/world/server/p2world.js @@ -51,6 +51,7 @@ class P2world extends World this.usePathFinder = sc.get(options, 'usePathFinder', true); this.respawnAreas = false; this.removeBodies = []; + this.removeBulletsStateIds = []; this.type = sc.get(options, 'type', WorldConst.TYPES.NO_GRAVITY_2D); this.enablePathFinder(); } @@ -113,20 +114,18 @@ class P2world extends World async createLayerContents(layer) { - let mapW = this.mapJson.width, - mapH = this.mapJson.height, - tileW = this.mapJson.tilewidth, + let tileW = this.mapJson.tilewidth, tileH = this.mapJson.tileheight, halfTileW = tileW / 2, halfTileH = tileH / 2; // loop columns: - for(let c = 0; c < mapW; c++){ + for(let c = 0; c < this.mapJson.width; c++){ let posX = c * tileW + halfTileW; // loop rows: - for(let r = 0; r < mapH; r++){ + for(let r = 0; r < this.mapJson.height; r++){ // position in pixels: let posY = r * tileH + halfTileH; - let tileIndex = r * mapW + c; + let tileIndex = this.tileIndexByRowAndColumn(r, c); let tile = layer.data[tileIndex]; let isZeroTile = 0 === Number(tile); let isChangePoint = false; @@ -145,6 +144,11 @@ class P2world extends World } } + tileIndexByRowAndColumn(r, c) + { + return r * this.mapJson.width + c; + } + validateMapData(mapJson) { return 0 < Number(mapJson.width || 0) @@ -169,7 +173,7 @@ class P2world extends World if(roomObject.multiple){ return; } - await this.createWorldObject(roomObject, objectIndex, tileW, tileH, posX, posY); + return await this.createWorldObject(roomObject, objectIndex, tileW, tileH, posX, posY); } markPathFinderTile(layer, isZeroTile, isChangePoint, isCollisionBody, c, r) @@ -241,11 +245,8 @@ class P2world extends World } // by default objects won't have mass: let bodyMass = sc.get(roomObject, 'bodyMass', 0); - // by default objects collision response: - let collision = sc.hasOwn(roomObject, 'collisionResponse', false); - // object state: + let collision = sc.get(roomObject, 'collisionResponse', false); let hasState = sc.get(roomObject, 'hasState', false); - // create the body: let bodyObject = this.createCollisionBody(tileW, tileH, posX, posY, bodyMass, collision, hasState, objectIndex); bodyObject.isRoomObject = true; // assign the room object to the body: @@ -273,6 +274,7 @@ class P2world extends World posY, pathFinder }); + return roomObject; } createLimits() @@ -432,7 +434,9 @@ class P2world extends World this.addBody(bulletBody); // and state on room map schema: // @NOTE: this index here will be the animation key since the bullet state doesn't have a key property. - bulletObject.room.state.bodies[bulletKey+'_bullet_'+bulletBody.id] = bulletBody.bodyState; + let bodyStateId = bulletKey+'_bullet_'+bulletBody.id; + bulletBody.bodyStateId = bodyStateId; + bulletObject.room.state.bodies[bodyStateId] = bulletBody.bodyState; // then speed up in the target direction: bulletBody.angle = Math.atan2(y, x) * 180 / Math.PI; bulletBody.velocity[0] = bulletObject.magnitude * Math.cos(angleByVelocity); diff --git a/lib/world/server/physical-body.js b/lib/world/server/physical-body.js index 1866bc469..de4db4120 100644 --- a/lib/world/server/physical-body.js +++ b/lib/world/server/physical-body.js @@ -2,9 +2,6 @@ * * Reldens - PlayerBody * - * Extended the physics P2JS Body to easily update the player state schema and get all bodies position updated - * automatically. - * */ const { Body, vec2 } = require('p2'); @@ -134,12 +131,16 @@ class PhysicalBody extends Body return; } if( - this.position[0] < 0 + 0 > this.position[0] || this.position[0] > (this.worldWidth * this.worldTileWidth) - || this.position[1] < 0 + || 0 > this.position[1] || this.position[1] > (this.worldHeight * this.worldTileHeight) + || (0 === this.velocity[0] && 0 === this.velocity[1]) ){ this.world.removeBodies.push(this); + if(this.bodyStateId){ + this.world.removeBulletsStateIds.push(this.bodyStateId); + } } } @@ -158,6 +159,7 @@ class PhysicalBody extends Body initMove(direction, isAuto = false) { + this.stopFull(); if(!isAuto){ // if user moves the player then reset the auto move. this.resetAuto(); diff --git a/migrations/development/20190923183906_v4.0.0.js b/migrations/development/20190923183906_v4.0.0.js new file mode 100644 index 000000000..b316fa317 --- /dev/null +++ b/migrations/development/20190923183906_v4.0.0.js @@ -0,0 +1,10 @@ +const fs = require('fs'); + +exports.up = function(knex) { + let sql = fs.readFileSync(__dirname+'/reldens-install-v4.0.0.sql').toString(); + return knex.raw(sql); +}; + +exports.down = function(knex) { + // nothing to do in the first version. +}; diff --git a/migrations/development/reldens-install-v4.0.0.sql b/migrations/development/reldens-install-v4.0.0.sql new file mode 100644 index 000000000..697a77d0a --- /dev/null +++ b/migrations/development/reldens-install-v4.0.0.sql @@ -0,0 +1,1386 @@ +-- -------------------------------------------------------- +-- Host: localhost +-- ServerPlugin version: 8.0.27 - MySQL Community ServerPlugin - GPL +-- ServerPlugin OS: Win64 +-- HeidiSQL Version: 11.3.0.6295 +-- -------------------------------------------------------- + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET NAMES utf8 */; +/*!50503 SET NAMES utf8mb4 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- Dumping structure for table audio +CREATE TABLE IF NOT EXISTS `audio` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `audio_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `files_name` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `config` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `room_id` int unsigned DEFAULT NULL, + `category_id` int unsigned DEFAULT NULL, + `enabled` int unsigned DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `audio_key` (`audio_key`), + KEY `FK_audio_rooms` (`room_id`), + KEY `FK_audio_audio_categories` (`category_id`), + CONSTRAINT `FK_audio_audio_categories` FOREIGN KEY (`category_id`) REFERENCES `audio_categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_audio_rooms` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) ON DELETE SET NULL ON UPDATE SET NULL +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table audio: ~2 rows (approximately) +/*!40000 ALTER TABLE `audio` DISABLE KEYS */; +INSERT INTO `audio` (`id`, `audio_key`, `files_name`, `config`, `room_id`, `category_id`, `enabled`) VALUES + (3, 'footstep', 'footstep.mp3', NULL, NULL, 3, 1), + (4, 'ReldensTownAudio', 'reldens-town.mp3', '', 4, 1, 1); +/*!40000 ALTER TABLE `audio` ENABLE KEYS */; + +-- Dumping structure for table audio_categories +CREATE TABLE IF NOT EXISTS `audio_categories` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `category_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `category_label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `enabled` int NOT NULL DEFAULT '0', + `single_audio` int NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `category_key` (`category_key`), + UNIQUE KEY `category_label` (`category_label`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table audio_categories: ~2 rows (approximately) +/*!40000 ALTER TABLE `audio_categories` DISABLE KEYS */; +INSERT INTO `audio_categories` (`id`, `category_key`, `category_label`, `enabled`, `single_audio`) VALUES + (1, 'music', 'Music', 1, 1), + (3, 'sound', 'Sound', 1, 0); +/*!40000 ALTER TABLE `audio_categories` ENABLE KEYS */; + +-- Dumping structure for table audio_markers +CREATE TABLE IF NOT EXISTS `audio_markers` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `audio_id` int unsigned NOT NULL, + `marker_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `start` int unsigned NOT NULL, + `duration` int unsigned NOT NULL, + `config` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`), + UNIQUE KEY `audio_id_marker_key` (`audio_id`,`marker_key`), + KEY `audio_id` (`audio_id`), + CONSTRAINT `FK_audio_markers_audio` FOREIGN KEY (`audio_id`) REFERENCES `audio` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table audio_markers: ~41 rows (approximately) +/*!40000 ALTER TABLE `audio_markers` DISABLE KEYS */; +INSERT INTO `audio_markers` (`id`, `audio_id`, `marker_key`, `start`, `duration`, `config`) VALUES + (1, 4, 'ReldensTown', 0, 41, NULL), + (2, 3, 'journeyman_right', 0, 1, NULL), + (3, 3, 'journeyman_left', 0, 1, NULL), + (4, 3, 'journeyman_up', 0, 1, NULL), + (5, 3, 'journeyman_down', 0, 1, NULL), + (6, 3, 'r_journeyman_right', 0, 1, NULL), + (7, 3, 'r_journeyman_left', 0, 1, NULL), + (8, 3, 'r_journeyman_up', 0, 1, NULL), + (9, 3, 'r_journeyman_down', 0, 1, NULL), + (10, 3, 'sorcerer_right', 0, 1, NULL), + (11, 3, 'sorcerer_left', 0, 1, NULL), + (12, 3, 'sorcerer_up', 0, 1, NULL), + (13, 3, 'sorcerer_down', 0, 1, NULL), + (14, 3, 'r_sorcerer_right', 0, 1, NULL), + (15, 3, 'r_sorcerer_left', 0, 1, NULL), + (16, 3, 'r_sorcerer_up', 0, 1, NULL), + (17, 3, 'r_sorcerer_down', 0, 1, NULL), + (18, 3, 'warlock_right', 0, 1, NULL), + (19, 3, 'warlock_left', 0, 1, NULL), + (20, 3, 'warlock_up', 0, 1, NULL), + (21, 3, 'warlock_down', 0, 1, NULL), + (22, 3, 'r_warlock_right', 0, 1, NULL), + (23, 3, 'r_warlock_left', 0, 1, NULL), + (24, 3, 'r_warlock_up', 0, 1, NULL), + (25, 3, 'r_warlock_down', 0, 1, NULL), + (26, 3, 'swordsman_right', 0, 1, NULL), + (27, 3, 'swordsman_left', 0, 1, NULL), + (28, 3, 'swordsman_up', 0, 1, NULL), + (29, 3, 'swordsman_down', 0, 1, NULL), + (30, 3, 'r_swordsman_right', 0, 1, NULL), + (31, 3, 'r_swordsman_left', 0, 1, NULL), + (32, 3, 'r_swordsman_up', 0, 1, NULL), + (33, 3, 'r_swordsman_down', 0, 1, NULL), + (34, 3, 'warrior_right', 0, 1, NULL), + (35, 3, 'warrior_left', 0, 1, NULL), + (36, 3, 'warrior_up', 0, 1, NULL), + (37, 3, 'warrior_down', 0, 1, NULL), + (38, 3, 'r_warrior_right', 0, 1, NULL), + (39, 3, 'r_warrior_left', 0, 1, NULL), + (40, 3, 'r_warrior_up', 0, 1, NULL), + (41, 3, 'r_warrior_down', 0, 1, NULL); +/*!40000 ALTER TABLE `audio_markers` ENABLE KEYS */; + +-- Dumping structure for table audio_player_config +CREATE TABLE IF NOT EXISTS `audio_player_config` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `player_id` int unsigned NOT NULL, + `category_id` int unsigned DEFAULT NULL, + `enabled` int unsigned DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `player_id_category_id` (`player_id`,`category_id`), + KEY `FK_audio_player_config_audio_categories` (`category_id`), + CONSTRAINT `FK_audio_player_config_audio_categories` FOREIGN KEY (`category_id`) REFERENCES `audio_categories` (`id`) ON UPDATE CASCADE, + CONSTRAINT `FK_audio_player_config_players` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table audio_player_config: ~0 rows (approximately) +/*!40000 ALTER TABLE `audio_player_config` DISABLE KEYS */; +/*!40000 ALTER TABLE `audio_player_config` ENABLE KEYS */; + +-- Dumping structure for table chat +CREATE TABLE IF NOT EXISTS `chat` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `player_id` int unsigned NOT NULL, + `room_id` int unsigned DEFAULT NULL, + `message` varchar(140) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `private_player_id` int unsigned DEFAULT NULL, + `message_type` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `message_time` timestamp NOT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`player_id`), + KEY `scene_id` (`room_id`), + KEY `private_user_id` (`private_player_id`), + CONSTRAINT `FK__players` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`), + CONSTRAINT `FK__players_2` FOREIGN KEY (`private_player_id`) REFERENCES `players` (`id`), + CONSTRAINT `FK__scenes` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table chat: ~0 rows (approximately) +/*!40000 ALTER TABLE `chat` DISABLE KEYS */; +/*!40000 ALTER TABLE `chat` ENABLE KEYS */; + +-- Dumping structure for table config +CREATE TABLE IF NOT EXISTS `config` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `scope` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `path` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `value` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `type` varchar(2) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=253 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table config: ~236 rows (approximately) +/*!40000 ALTER TABLE `config` DISABLE KEYS */; +INSERT INTO `config` (`id`, `scope`, `path`, `value`, `type`) VALUES + (1, 'server', 'rooms/validation/valid', 'room_game,chat_global', 't'), + (2, 'server', 'players/initialState/room_id', '4', 'i'), + (3, 'server', 'players/initialState/x', '400', 'i'), + (4, 'server', 'players/initialState/y', '345', 'i'), + (5, 'server', 'players/initialState/dir', 'down', 't'), + (13, 'server', 'rooms/validation/enabled', '1', 'b'), + (14, 'server', 'rooms/world/gravity_enabled', '0', 'b'), + (16, 'server', 'players/size/width', '25', 'i'), + (17, 'server', 'players/size/height', '25', 'i'), + (18, 'server', 'general/controls/allow_simultaneous_keys', '1', 'b'), + (19, 'server', 'rooms/world/timestep', '0.04', 'i'), + (20, 'server', 'chat/messages/broadcast_join', '1', 'b'), + (21, 'server', 'chat/messages/broadcast_leave', '1', 'b'), + (22, 'server', 'chat/messages/global_enabled', '1', 'b'), + (23, 'server', 'chat/messages/global_allowed_roles', '1,9000', 't'), + (24, 'server', 'players/physicsBody/speed', '180', 'i'), + (25, 'client', 'players/animations/fadeDuration', '1000', 'i'), + (26, 'client', 'ui/playerBox/x', '50', 'i'), + (27, 'client', 'ui/playerStats/enabled', '1', 'b'), + (28, 'client', 'ui/controls/enabled', '1', 'b'), + (29, 'client', 'map/tileData/width', '16', 'i'), + (30, 'client', 'map/tileData/height', '16', 'i'), + (31, 'client', 'map/tileData/margin', '1', 'i'), + (32, 'client', 'map/tileData/spacing', '2', 'i'), + (33, 'client', 'players/size/width', '52', 'i'), + (34, 'client', 'players/size/height', '71', 'i'), + (35, 'client', 'general/animations/frameRate', '10', 'i'), + (36, 'client', 'map/layersDepth/belowPlayer', '0', 'i'), + (37, 'client', 'map/layersDepth/changePoints', '0', 'i'), + (38, 'client', 'ui/sceneLabel/enabled', '1', 'b'), + (39, 'client', 'general/controls/action_button_hold', '0', 'b'), + (40, 'client', 'ui/chat/x', '440', 'i'), + (41, 'client', 'ui/chat/y', '940', 'i'), + (42, 'server', 'players/actions/interactionDistance', '40', 'i'), + (43, 'server', 'objects/actions/interactionsDistance', '140', 'i'), + (44, 'client', 'ui/playerBox/enabled', '1', 'b'), + (45, 'client', 'ui/playerBox/y', '30', 'i'), + (46, 'client', 'ui/lifeBar/enabled', '1', 'b'), + (47, 'client', 'ui/uiTarget/x', '10', 'i'), + (48, 'client', 'ui/uiTarget/y', '85', 'i'), + (49, 'client', 'ui/sceneLabel/x', '250', 'i'), + (50, 'client', 'ui/sceneLabel/y', '20', 'i'), + (51, 'client', 'ui/controls/x', '120', 'i'), + (52, 'client', 'ui/controls/y', '390', 'i'), + (53, 'client', 'ui/playerStats/x', '430', 'i'), + (54, 'client', 'ui/playerStats/y', '20', 'i'), + (55, 'client', 'ui/loading/font', 'Verdana, Geneva, sans-serif', 't'), + (56, 'client', 'ui/loading/fontSize', '20px', 't'), + (57, 'client', 'ui/loading/assetsSize', '18px', 't'), + (58, 'client', 'ui/loading/loadingColor', '#ffffff', 't'), + (59, 'client', 'ui/loading/percentColor', '#666666', 't'), + (60, 'client', 'ui/loading/assetsColor', '#ffffff', 't'), + (61, 'client', 'ui/loading/showAssets', '1', 'b'), + (62, 'client', 'players/animations/basedOnPress', '1', 'b'), + (63, 'client', 'players/animations/diagonalHorizontal', '1', 'b'), + (64, 'client', 'ui/uiTarget/hideOnDialog', '0', 'b'), + (65, 'client', 'ui/uiTarget/enabled', '1', 'b'), + (66, 'client', 'ui/lifeBar/x', '5', 'i'), + (67, 'client', 'ui/lifeBar/y', '12', 'i'), + (68, 'client', 'ui/lifeBar/height', '5', 'i'), + (69, 'client', 'ui/lifeBar/width', '50', 'i'), + (70, 'client', 'ui/lifeBar/fixedPosition', '0', 'b'), + (71, 'server', 'rooms/world/tryClosestPath', '1', 'b'), + (72, 'server', 'actions/pvp/battleTimeOff', '20000', 'i'), + (73, 'server', 'actions/pvp/timerType', 'bt', 's'), + (74, 'server', 'enemies/initialStats/atk', '10', 'i'), + (75, 'server', 'enemies/initialStats/def', '10', 'i'), + (76, 'server', 'enemies/initialStats/dodge', '10', 'i'), + (77, 'server', 'enemies/initialStats/hp', '10', 'i'), + (78, 'server', 'enemies/initialStats/mp', '10', 'i'), + (79, 'server', 'enemies/initialStats/speed', '10', 'i'), + (80, 'server', 'enemies/initialStats/stamina', '10', 'i'), + (81, 'client', 'ui/pointer/show', '1', 'b'), + (82, 'server', 'enemies/defaultAttacks/attackBullet', '0', 'b'), + (83, 'client', 'players/size/topOffset', '20', 'i'), + (84, 'client', 'players/size/leftOffset', '0', 'i'), + (85, 'server', 'rooms/world/onlyWalkable', '1', 'b'), + (86, 'client', 'ui/screen/responsive', '1', 'b'), + (87, 'client', 'ui/uiTarget/responsiveY', '0', 'i'), + (88, 'client', 'ui/uiTarget/responsiveX', '0', 'i'), + (89, 'client', 'ui/inventory/enabled', '1', 'b'), + (90, 'client', 'ui/inventory/x', '380', 'i'), + (91, 'client', 'ui/inventory/y', '450', 'i'), + (92, 'client', 'ui/inventory/responsiveY', '0', 'i'), + (93, 'client', 'ui/inventory/responsiveX', '100', 'i'), + (94, 'client', 'ui/equipment/enabled', '1', 'b'), + (95, 'client', 'ui/equipment/x', '430', 'i'), + (96, 'client', 'ui/equipment/y', '90', 'i'), + (97, 'client', 'ui/equipment/responsiveY', '0', 'i'), + (98, 'client', 'ui/equipment/responsiveX', '100', 'i'), + (99, 'client', 'ui/lifeBar/responsiveY', '24', 'i'), + (100, 'client', 'ui/lifeBar/responsiveX', '1', 'i'), + (101, 'client', 'ui/sceneLabel/responsiveY', '0', 'i'), + (102, 'client', 'ui/sceneLabel/responsiveX', '50', 'i'), + (103, 'client', 'ui/playerStats/responsiveY', '0', 'i'), + (104, 'client', 'ui/playerStats/responsiveX', '100', 'i'), + (105, 'client', 'ui/playerBox/responsiveY', '0', 'i'), + (106, 'client', 'ui/playerBox/responsiveX', '0', 'i'), + (107, 'client', 'ui/controls/responsiveY', '100', 'i'), + (108, 'client', 'ui/controls/responsiveX', '0', 'i'), + (109, 'client', 'ui/chat/responsiveY', '100', 'i'), + (110, 'client', 'ui/chat/responsiveX', '100', 'i'), + (111, 'client', 'ui/chat/enabled', '1', 'b'), + (112, 'client', 'ui/npcDialog/x', '120', 'i'), + (113, 'client', 'ui/npcDialog/y', '100', 'i'), + (114, 'client', 'ui/npcDialog/responsiveX', '10', 'i'), + (115, 'client', 'ui/npcDialog/responsiveY', '10', 'i'), + (116, 'client', 'ui/maximum/x', '1280', 'i'), + (117, 'client', 'ui/maximum/y', '720', 'i'), + (118, 'client', 'ui/chat/defaultOpen', '0', 'b'), + (119, 'client', 'ui/chat/notificationBalloon', '1', 'b'), + (120, 'client', 'ui/chat/damageMessages', '1', 'b'), + (121, 'server', 'players/actions/initialClassPathId', '1', 'i'), + (122, 'server', 'enemies/initialStats/aim', '10', 'i'), + (123, 'client', 'actions/skills/affectedProperty', 'hp', 't'), + (124, 'client', 'ui/controls/opacityEffect', '1', 'b'), + (125, 'client', 'ui/skills/y', '390', 'i'), + (126, 'client', 'ui/skills/x', '230', 'i'), + (127, 'client', 'ui/skills/responsiveY', '100', 'i'), + (128, 'client', 'ui/skills/responsiveX', '0', 'i'), + (129, 'client', 'ui/skills/enabled', '1', 'b'), + (130, 'client', 'skills/animations/default_atk', '{"key":"default_atk","animationData":{"enabled":true,"type":"spritesheet","img":"default_atk","frameWidth":64,"frameHeight":64,"start":0,"end":4,"repeat":0}}', 'j'), + (131, 'client', 'skills/animations/default_bullet', '{"key":"default_bullet","animationData":{"enabled":true,"type":"spritesheet","img":"default_bullet","frameWidth":64,"frameHeight":64,"start":0,"end":2,"repeat":-1,"rate":1}}', 'j'), + (132, 'client', 'skills/animations/default_cast', '{"key": "default_cast","animationData":{"enabled":false,"type":"spritesheet","img":"default_cast","frameWidth":64,"frameHeight":64,"start":0,"end":3,"repeat":0}}', 'j'), + (133, 'client', 'skills/animations/default_death', '{"key":"default_death","animationData":{"enabled":true,"type":"spritesheet","img":"default_death","frameWidth":64,"frameHeight":64,"start":0,"end":1,"repeat":0,"rate":1}}', 'j'), + (134, 'client', 'skills/animations/default_hit', '{"key":"default_hit","animationData":{"enabled":true,"type":"spritesheet","img":"default_hit","frameWidth":64,"frameHeight":64,"start":0,"end":3,"repeat":0,"depthByPlayer":"above"}}', 'j'), + (135, 'client', 'ui/controls/defaultActionKey', '', 't'), + (136, 'client', 'players/animations/collideWorldBounds', '1', 'b'), + (137, 'server', 'rooms/world/bulletsStopOnPlayer', '1', 'b'), + (138, 'client', 'players/animations/fallbackImage', 'player-base', 't'), + (139, 'client', 'players/multiplePlayers/enabled', '1', 'b'), + (140, 'server', 'players/gameOver/timeOut', '10000', 'i'), + (141, 'client', 'ui/controls/tabTarget', '1', 'b'), + (142, 'client', 'ui/controls/disableContextMenu', '1', 'b'), + (143, 'client', 'ui/controls/primaryMove', '1', 'b'), + (144, 'client', 'ui/instructions/enabled', '1', 'b'), + (145, 'client', 'ui/instructions/responsiveX', '100', 'i'), + (146, 'client', 'ui/instructions/responsiveY', '100', 'i'), + (147, 'client', 'ui/instructions/x', '380', 'i'), + (148, 'client', 'ui/instructions/y', '940', 'i'), + (149, 'client', 'ui/players/showNames', '1', 'b'), + (157, 'client', 'ui/lifeBar/top', '5', 'i'), + (158, 'client', 'actions/damage/enabled', '1', 'b'), + (159, 'client', 'actions/damage/showAll', '0', 'b'), + (160, 'client', 'actions/damage/font', 'Verdana, Geneva, sans-serif', 't'), + (161, 'client', 'actions/damage/color', '#ff0000', 't'), + (162, 'client', 'actions/damage/duration', '600', 'i'), + (163, 'client', 'actions/damage/top', '50', 'i'), + (164, 'client', 'actions/damage/fontSize', '14', 'i'), + (165, 'client', 'actions/damage/stroke', '#000000', 't'), + (166, 'client', 'actions/damage/strokeThickness', '4', 'i'), + (167, 'client', 'actions/damage/shadowColor', 'rgba(0,0,0,0.7)', 't'), + (168, 'client', 'ui/lifeBar/fillStyle', '0xff0000', 't'), + (169, 'client', 'ui/lifeBar/lineStyle', '0xffffff', 't'), + (170, 'client', 'ui/lifeBar/showAllPlayers', '0', 'b'), + (171, 'client', 'ui/lifeBar/showEnemies', '1', 'b'), + (172, 'client', 'players/animations/defaultFrames/left/start', '3', 'i'), + (173, 'client', 'players/animations/defaultFrames/left/end', '5', 'i'), + (174, 'client', 'players/animations/defaultFrames/right/start', '6', 'i'), + (175, 'client', 'players/animations/defaultFrames/right/end', '8', 'i'), + (176, 'client', 'players/animations/defaultFrames/up/start', '9', 'i'), + (177, 'client', 'players/animations/defaultFrames/up/end', '11', 'i'), + (178, 'client', 'players/animations/defaultFrames/down/start', '0', 'i'), + (179, 'client', 'players/animations/defaultFrames/down/end', '2', 'i'), + (180, 'client', 'ui/minimap/enabled', '1', 'b'), + (181, 'client', 'ui/minimap/mapWidthDivisor', '1', 'i'), + (182, 'client', 'ui/minimap/mapHeightDivisor', '1', 'i'), + (183, 'client', 'ui/minimap/fixedWidth', '450', 'i'), + (184, 'client', 'ui/minimap/fixedHeight', '450', 'i'), + (185, 'client', 'ui/minimap/roundMap', '1', 'b'), + (186, 'client', 'ui/minimap/camX', '140', 'i'), + (187, 'client', 'ui/minimap/camY', '10', 'i'), + (188, 'client', 'ui/minimap/camBackgroundColor', 'rgba(0,0,0,0.6)', 't'), + (189, 'client', 'ui/minimap/camZoom', '0.35', 'i'), + (190, 'client', 'ui/minimap/roundMap', '1', 'b'), + (191, 'client', 'ui/minimap/addCircle', '1', 'b'), + (192, 'client', 'ui/minimap/circleX', '220', 'i'), + (193, 'client', 'ui/minimap/circleY', '88', 'i'), + (194, 'client', 'ui/minimap/circleRadio', '80.35', 'i'), + (195, 'client', 'ui/minimap/circleColor', 'rgb(0,0,0)', 't'), + (196, 'client', 'ui/minimap/circleAlpha', '1', 'i'), + (197, 'client', 'ui/minimap/circleStrokeLineWidth', '6', 'i'), + (198, 'client', 'ui/minimap/circleStrokeColor', '0', 'i'), + (199, 'client', 'ui/minimap/circleStrokeAlpha', '0.6', 'i'), + (200, 'client', 'ui/minimap/circleFillColor', '1', 'i'), + (201, 'client', 'ui/minimap/circleFillAlpha', '0', 'i'), + (202, 'client', 'ui/pointer/topOffSet', '16', 'i'), + (203, 'client', 'ui/minimap/responsiveX', '34', 'i'), + (204, 'client', 'ui/minimap/responsiveY', '2.4', 'i'), + (205, 'client', 'ui/minimap/x', '180', 'i'), + (206, 'client', 'ui/minimap/y', '10', 'i'), + (207, 'client', 'ui/settings/responsiveX', '100', 'i'), + (208, 'client', 'ui/settings/responsiveY', '100', 'i'), + (209, 'client', 'ui/settings/x', '940', 'i'), + (210, 'client', 'ui/settings/y', '280', 'i'), + (211, 'client', 'ui/settings/enabled', '1', 'b'), + (212, 'client', 'ui/lifeBar/showOnClick', '1', 'b'), + (213, 'client', 'rooms/selection/allowOnRegistration', '1', 'b'), + (214, 'client', 'rooms/selection/allowOnLogin', '1', 'b'), + (215, 'client', 'rooms/selection/registrationAvailableRooms', '*', 't'), + (216, 'client', 'rooms/selection/loginLastLocation', '1', 'b'), + (218, 'client', 'rooms/selection/loginAvailableRooms', '*', 't'), + (219, 'client', 'rooms/selection/loginLastLocationLabel', 'Last Location', 't'), + (220, 'client', 'players/tapMovement/enabled', '1', 'b'), + (221, 'client', 'ui/chat/overheadChat/enabled', '1', 'b'), + (222, 'client', 'chat/messages/characterLimit', '10', 'i'), + (223, 'client', 'chat/messages/characterLimitOverhead', '5', 'i'), + (224, 'client', 'ui/chat/overheadText/fontFamily', 'Verdana, Geneva, sans-serif', 't'), + (225, 'client', 'ui/chat/overheadText/fontSize', '12px', 't'), + (226, 'client', 'ui/chat/overheadText/fill', '#ffffff', 't'), + (227, 'client', 'ui/chat/overheadText/align', 'center', 't'), + (228, 'client', 'ui/chat/overheadText/stroke', 'rgba(0,0,0,0.7)', 't'), + (229, 'client', 'ui/chat/overheadText/strokeThickness', '20', 'i'), + (230, 'client', 'ui/chat/overheadText/shadowX', '5', 'i'), + (231, 'client', 'ui/chat/overheadText/shadowY', '5', 'i'), + (232, 'client', 'ui/chat/overheadText/shadowColor', 'rgba(0,0,0,0.7)', 't'), + (233, 'client', 'ui/chat/overheadText/shadowBlur', '5', 'i'), + (234, 'client', 'ui/chat/overheadText/depth', '200000', 'i'), + (235, 'client', 'ui/chat/overheadText/height', '15', 'i'), + (236, 'client', 'ui/chat/overheadText/textLength', '4', 'i'), + (237, 'client', 'ui/players/nameText/fontFamily', 'Verdana, Geneva, sans-serif', 't'), + (238, 'client', 'ui/players/nameText/fontSize', '12px', 't'), + (239, 'client', 'ui/players/nameText/fill', '#ffffff', 't'), + (240, 'client', 'ui/players/nameText/align', 'center', 't'), + (241, 'client', 'ui/players/nameText/stroke', '#000000', 't'), + (242, 'client', 'ui/players/nameText/strokeThickness', '4', 'i'), + (243, 'client', 'ui/players/nameText/shadowX', '5', 'i'), + (244, 'client', 'ui/players/nameText/shadowY', '5', 'i'), + (245, 'client', 'ui/players/nameText/shadowColor', 'rgba(0,0,0,0.7)', 't'), + (246, 'client', 'ui/players/nameText/shadowBlur', '5', 'i'), + (247, 'client', 'ui/players/nameText/depth', '200000', 'i'), + (248, 'client', 'ui/players/nameText/height', '-90', 'i'), + (249, 'client', 'ui/players/nameText/textLength', '4', 'i'), + (250, 'client', 'ui/chat/overheadChat/isTyping', '1', 'b'), + (251, 'client', 'ui/chat/overheadText/timeOut', '5000', 'i'), + (252, 'client', 'ui/chat/overheadChat/closeChatBoxAfterSend', '1', 'b'); +/*!40000 ALTER TABLE `config` ENABLE KEYS */; + +-- Dumping structure for table features +CREATE TABLE IF NOT EXISTS `features` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `is_enabled` int NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table features: ~10 rows (approximately) +/*!40000 ALTER TABLE `features` DISABLE KEYS */; +INSERT INTO `features` (`id`, `code`, `title`, `is_enabled`) VALUES + (1, 'chat', 'Chat', 1), + (2, 'objects', 'Objects', 1), + (3, 'respawn', 'Respawn', 1), + (4, 'inventory', 'Inventory', 1), + (5, 'firebase', 'Firebase', 1), + (6, 'actions', 'Actions', 1), + (7, 'users', 'Users', 1), + (8, 'audio', 'Audio', 1), + (9, 'rooms', 'Rooms', 1), + (10, 'admin', 'Admin', 1); +/*!40000 ALTER TABLE `features` ENABLE KEYS */; + +-- Dumping structure for table items_group +CREATE TABLE IF NOT EXISTS `items_group` ( + `id` int NOT NULL AUTO_INCREMENT, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `description` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `files_name` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `sort` int DEFAULT NULL, + `items_limit` int NOT NULL DEFAULT '0', + `limit_per_item` int NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='The group table is to save the groups settings.'; + +-- Dumping data for table items_group: ~6 rows (approximately) +/*!40000 ALTER TABLE `items_group` DISABLE KEYS */; +INSERT INTO `items_group` (`id`, `key`, `label`, `description`, `files_name`, `sort`, `items_limit`, `limit_per_item`) VALUES + (1, 'weapon', 'Weapon', 'All kinds of weapons.', 'weapon.png', 2, 1, 0), + (2, 'shield', 'Shield', 'Protect with these items.', 'shield.png', 3, 1, 0), + (3, 'armor', 'Armor', '', 'armor.png', 4, 1, 0), + (4, 'boots', 'Boots', '', 'boots.png', 6, 1, 0), + (5, 'gauntlets', 'Gauntlets', '', 'gauntlets.png', 5, 1, 0), + (6, 'helmet', 'Helmet', '', 'helmet.png', 1, 1, 0); +/*!40000 ALTER TABLE `items_group` ENABLE KEYS */; + +-- Dumping structure for table items_inventory +CREATE TABLE IF NOT EXISTS `items_inventory` ( + `id` int NOT NULL AUTO_INCREMENT, + `owner_id` int NOT NULL, + `item_id` int NOT NULL, + `qty` int NOT NULL DEFAULT '0', + `remaining_uses` int DEFAULT NULL, + `is_active` int DEFAULT NULL COMMENT 'For example equipped or not equipped items.', + PRIMARY KEY (`id`), + KEY `FK_items_inventory_items_item` (`item_id`), + CONSTRAINT `FK_items_inventory_items_item` FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='Inventory table is to save the items for each owner.'; + +-- Dumping data for table items_inventory: ~0 rows (approximately) +/*!40000 ALTER TABLE `items_inventory` DISABLE KEYS */; +/*!40000 ALTER TABLE `items_inventory` ENABLE KEYS */; + +-- Dumping structure for table items_item +CREATE TABLE IF NOT EXISTS `items_item` ( + `id` int NOT NULL AUTO_INCREMENT, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `type` int NOT NULL DEFAULT '0', + `group_id` int DEFAULT NULL, + `label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `qty_limit` int NOT NULL DEFAULT '0' COMMENT 'Default 0 to unlimited qty.', + `uses_limit` int NOT NULL DEFAULT '1' COMMENT 'Default 1 use per item (0 = unlimited).', + `useTimeOut` int DEFAULT NULL, + `execTimeOut` int DEFAULT NULL, + `customData` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`), + UNIQUE KEY `key` (`key`), + KEY `group_id` (`group_id`), + CONSTRAINT `FK_items_item_items_group` FOREIGN KEY (`group_id`) REFERENCES `items_group` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='List of all available items in the system.'; + +-- Dumping data for table items_item: ~6 rows (approximately) +/*!40000 ALTER TABLE `items_item` DISABLE KEYS */; +INSERT INTO `items_item` (`id`, `key`, `type`, `group_id`, `label`, `description`, `qty_limit`, `uses_limit`, `useTimeOut`, `execTimeOut`, `customData`) VALUES + (1, 'coins', 3, NULL, 'Coins', NULL, 0, 1, NULL, NULL, NULL), + (2, 'branch', 0, NULL, 'Tree branch', 'An useless tree branch (for now)', 0, 1, NULL, NULL, NULL), + (3, 'heal_potion_20', 5, NULL, 'Heal Potion', 'A heal potion that will restore 20 HP.', 0, 1, NULL, NULL, '{"removeAfterUse":true,"animationData":{"frameWidth":64,"frameHeight":64,"start":6,"end":11,"repeat":0,"hide":true,"destroyOnComplete":true,"usePlayerPosition":true,"closeInventoryOnUse":true,"followPlayer":true,"startsOnTarget":true}}'), + (4, 'axe', 4, 1, 'Axe', 'A short distance but powerful weapon.', 0, 0, NULL, NULL, '{"animationData":{"frameWidth":64,"frameHeight":64,"start":6,"end":11,"repeat":0,"hide":true,"destroyOnComplete":true,"usePlayerPosition":true,"closeInventoryOnUse":true,"followPlayer":true,"startsOnTarget":true}}'), + (5, 'spear', 4, 1, 'Spear', 'A short distance but powerful weapon.', 0, 0, NULL, NULL, '{"animationData":{"frameWidth":64,"frameHeight":64,"start":6,"end":11,"repeat":0,"hide":true,"destroyOnComplete":true,"usePlayerPosition":true,"closeInventoryOnUse":true,"followPlayer":true,"startsOnTarget":true}}'), + (6, 'magic_potion_20', 5, NULL, 'Magic Potion', 'A magic potion that will restore 20 MP.', 0, 1, NULL, NULL, '{"removeAfterUse":true,"animationData":{"frameWidth":64,"frameHeight":64,"start":6,"end":11,"repeat":0,"hide":true,"destroyOnComplete":true,"usePlayerPosition":true,"closeInventoryOnUse":true,"followPlayer":true,"startsOnTarget":true}}'); +/*!40000 ALTER TABLE `items_item` ENABLE KEYS */; + +-- Dumping structure for table items_item_modifiers +CREATE TABLE IF NOT EXISTS `items_item_modifiers` ( + `id` int NOT NULL AUTO_INCREMENT, + `item_id` int NOT NULL, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `operation` int NOT NULL, + `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `maxProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `item_id` (`item_id`), + CONSTRAINT `FK_items_item_modifiers_items_item` FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='Modifiers is the way we will affect the item owner.'; + +-- Dumping data for table items_item_modifiers: ~4 rows (approximately) +/*!40000 ALTER TABLE `items_item_modifiers` DISABLE KEYS */; +INSERT INTO `items_item_modifiers` (`id`, `item_id`, `key`, `property_key`, `operation`, `value`, `maxProperty`) VALUES + (1, 4, 'atk', 'stats/atk', 5, '5', NULL), + (2, 3, 'heal_potion_20', 'stats/hp', 1, '20', 'statsBase/hp'), + (3, 5, 'atk', 'stats/atk', 5, '3', NULL), + (4, 6, 'magic_potion_20', 'stats/mp', 1, '20', 'statsBase/mp'); +/*!40000 ALTER TABLE `items_item_modifiers` ENABLE KEYS */; + +-- Dumping structure for table objects +CREATE TABLE IF NOT EXISTS `objects` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `room_id` int unsigned NOT NULL, + `layer_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `tile_index` int unsigned DEFAULT NULL, + `object_class_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `client_key` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `private_params` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `client_params` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `enabled` int NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`), + UNIQUE KEY `room_id_layer_name_tile_index` (`room_id`,`layer_name`,`tile_index`), + KEY `room_id` (`room_id`), + KEY `object_class_key` (`object_class_key`), + CONSTRAINT `FK_objects_rooms` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table objects: ~8 rows (approximately) +/*!40000 ALTER TABLE `objects` DISABLE KEYS */; +INSERT INTO `objects` (`id`, `room_id`, `layer_name`, `tile_index`, `object_class_key`, `client_key`, `title`, `private_params`, `client_params`, `enabled`) VALUES + (1, 4, 'ground-collisions', 444, 'door_1', 'door_house_1', '', NULL, NULL, 1), + (4, 4, 'ground-collisions', 951, 'door_2', 'door_house_2', '', NULL, NULL, 1), + (5, 4, 'house-collisions-over-player', 535, 'npc_1', 'people_town_1', 'Alfred', NULL, NULL, 1), + (6, 5, 'respawn-area-monsters-lvl-1-2', NULL, 'enemy_1', 'enemy_forest_1', 'Tree', NULL, '{"autoStart":true}', 1), + (7, 5, 'respawn-area-monsters-lvl-1-2', NULL, 'enemy_2', 'enemy_forest_2', 'Tree Punch', NULL, '{"autoStart":true}', 1), + (8, 4, 'house-collisions-over-player', 538, 'npc_2', 'healer_1', 'Mamon', NULL, NULL, 1), + (10, 4, 'house-collisions-over-player', 560, 'npc_3', 'merchant_1', 'Gimly', NULL, NULL, 1), + (12, 4, 'house-collisions-over-player', 562, 'npc_4', 'weapons_master_1', 'Barrik', NULL, NULL, 1); +/*!40000 ALTER TABLE `objects` ENABLE KEYS */; + +-- Dumping structure for table objects_animations +CREATE TABLE IF NOT EXISTS `objects_animations` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `object_id` int unsigned NOT NULL, + `animationKey` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `animationData` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `object_id_animationKey` (`object_id`,`animationKey`), + KEY `id` (`id`) USING BTREE, + KEY `object_id` (`object_id`) USING BTREE, + CONSTRAINT `FK_objects_animations_objects` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table objects_animations: ~4 rows (approximately) +/*!40000 ALTER TABLE `objects_animations` DISABLE KEYS */; +INSERT INTO `objects_animations` (`id`, `object_id`, `animationKey`, `animationData`) VALUES + (5, 6, 'respawn-area-monsters-lvl-1-2_6_right', '{"start":6,"end":8}'), + (6, 6, 'respawn-area-monsters-lvl-1-2_6_down', '{"start":0,"end":2}'), + (7, 6, 'respawn-area-monsters-lvl-1-2_6_left', '{"start":3,"end":5}'), + (8, 6, 'respawn-area-monsters-lvl-1-2_6_up', '{"start":9,"end":11}'); +/*!40000 ALTER TABLE `objects_animations` ENABLE KEYS */; + +-- Dumping structure for table objects_assets +CREATE TABLE IF NOT EXISTS `objects_assets` ( + `object_asset_id` int unsigned NOT NULL AUTO_INCREMENT, + `object_id` int unsigned NOT NULL, + `asset_type` varchar(255) NOT NULL, + `asset_key` varchar(255) NOT NULL, + `file_1` varchar(255) NOT NULL, + `file_2` varchar(255) DEFAULT NULL, + `extra_params` text, + PRIMARY KEY (`object_asset_id`), + KEY `object_id` (`object_id`), + CONSTRAINT `FK_objects_assets_objects` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1; + +-- Dumping data for table objects_assets: ~8 rows (approximately) +/*!40000 ALTER TABLE `objects_assets` DISABLE KEYS */; +INSERT INTO `objects_assets` (`object_asset_id`, `object_id`, `asset_type`, `asset_key`, `file_1`, `file_2`, `extra_params`) VALUES + (1, 1, 'spritesheet', 'door_house_1', 'door-a-x2', NULL, '{"frameWidth":32,"frameHeight":58}'), + (2, 4, 'spritesheet', 'door_house_2', 'door-a-x2', NULL, '{"frameWidth":32,"frameHeight":58}'), + (3, 5, 'spritesheet', 'people_town_1', 'people-b-x2', NULL, '{"frameWidth":52,"frameHeight":71}'), + (5, 6, 'spritesheet', 'enemy_forest_1', 'monster-treant', NULL, '{"frameWidth":47,"frameHeight":50}'), + (6, 7, 'spritesheet', 'enemy_forest_2', 'monster-golem2', NULL, '{"frameWidth":47,"frameHeight":50}'), + (7, 5, 'spritesheet', 'healer_1', 'healer-1', NULL, '{"frameWidth":52,"frameHeight":71}'), + (9, 10, 'spritesheet', 'merchant_1', 'people-d-x2', NULL, '{"frameWidth":52,"frameHeight":71}'), + (10, 12, 'spritesheet', 'weapons_master_1', 'people-c-x2', NULL, '{"frameWidth":52,"frameHeight":71}'); +/*!40000 ALTER TABLE `objects_assets` ENABLE KEYS */; + +-- Dumping structure for table players +CREATE TABLE IF NOT EXISTS `players` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `user_id` int unsigned NOT NULL, + `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`), + KEY `FK_players_users` (`user_id`), + CONSTRAINT `FK_players_users` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table players: ~0 rows (approximately) +/*!40000 ALTER TABLE `players` DISABLE KEYS */; +/*!40000 ALTER TABLE `players` ENABLE KEYS */; + +-- Dumping structure for table players_state +CREATE TABLE IF NOT EXISTS `players_state` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `player_id` int unsigned NOT NULL, + `room_id` int unsigned NOT NULL, + `x` int unsigned NOT NULL, + `y` int unsigned NOT NULL, + `dir` varchar(25) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `FK_player_state_rooms` (`room_id`), + KEY `FK_player_state_player_stats` (`player_id`), + CONSTRAINT `FK_player_state_player_stats` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON UPDATE CASCADE, + CONSTRAINT `FK_player_state_rooms` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table players_state: ~0 rows (approximately) +/*!40000 ALTER TABLE `players_state` DISABLE KEYS */; +/*!40000 ALTER TABLE `players_state` ENABLE KEYS */; + +-- Dumping structure for table players_stats +CREATE TABLE IF NOT EXISTS `players_stats` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `player_id` int unsigned NOT NULL, + `stat_id` int unsigned NOT NULL, + `base_value` int unsigned NOT NULL, + `value` int unsigned NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `player_id_stat_id` (`player_id`,`stat_id`) USING BTREE, + KEY `stat_id` (`stat_id`) USING BTREE, + KEY `user_id` (`player_id`) USING BTREE, + CONSTRAINT `FK_player_current_stats_players` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT `FK_players_current_stats_players_stats` FOREIGN KEY (`stat_id`) REFERENCES `stats` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table players_stats: ~0 rows (approximately) +/*!40000 ALTER TABLE `players_stats` DISABLE KEYS */; +/*!40000 ALTER TABLE `players_stats` ENABLE KEYS */; + +-- Dumping structure for table respawn +CREATE TABLE IF NOT EXISTS `respawn` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `object_id` int unsigned NOT NULL, + `respawn_time` int unsigned NOT NULL DEFAULT '0', + `instances_limit` int unsigned NOT NULL DEFAULT '0', + `layer` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `respawn_object_id` (`object_id`), + CONSTRAINT `FK_respawn_objects` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table respawn: ~2 rows (approximately) +/*!40000 ALTER TABLE `respawn` DISABLE KEYS */; +INSERT INTO `respawn` (`id`, `object_id`, `respawn_time`, `instances_limit`, `layer`) VALUES + (3, 6, 20000, 2, 'respawn-area-monsters-lvl-1-2'), + (4, 7, 10000, 3, 'respawn-area-monsters-lvl-1-2'); +/*!40000 ALTER TABLE `respawn` ENABLE KEYS */; + +-- Dumping structure for table rooms +CREATE TABLE IF NOT EXISTS `rooms` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `map_filename` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'The map JSON file name.', + `scene_images` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `room_class_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table rooms: ~5 rows (approximately) +/*!40000 ALTER TABLE `rooms` DISABLE KEYS */; +INSERT INTO `rooms` (`id`, `name`, `title`, `map_filename`, `scene_images`, `room_class_key`) VALUES + (2, 'ReldensHouse_1', 'House - 1', 'reldens-house-1', 'reldens-house-1', NULL), + (3, 'ReldensHouse_2', 'House - 2', 'reldens-house-2', 'reldens-house-2', NULL), + (4, 'ReldensTown', 'Town', 'reldens-town', 'reldens-town', NULL), + (5, 'ReldensForest', 'Forest', 'reldens-forest', 'reldens-forest', NULL), + (6, 'ReldensHouse_1b', 'House - 1 - Floor 2', 'reldens-house-1-2d-floor', 'reldens-house-1-2d-floor', NULL); +/*!40000 ALTER TABLE `rooms` ENABLE KEYS */; + +-- Dumping structure for table rooms_change_points +CREATE TABLE IF NOT EXISTS `rooms_change_points` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `room_id` int unsigned NOT NULL, + `tile_index` int unsigned NOT NULL, + `next_room_id` int unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`), + KEY `scene_id` (`room_id`), + KEY `FK_rooms_change_points_rooms_2` (`next_room_id`), + CONSTRAINT `FK_rooms_change_points_rooms` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) ON UPDATE CASCADE, + CONSTRAINT `FK_rooms_change_points_rooms_2` FOREIGN KEY (`next_room_id`) REFERENCES `rooms` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table rooms_change_points: ~14 rows (approximately) +/*!40000 ALTER TABLE `rooms_change_points` DISABLE KEYS */; +INSERT INTO `rooms_change_points` (`id`, `room_id`, `tile_index`, `next_room_id`) VALUES + (1, 2, 816, 4), + (2, 2, 817, 4), + (3, 3, 778, 4), + (4, 3, 779, 4), + (5, 4, 444, 2), + (6, 4, 951, 3), + (7, 4, 18, 5), + (8, 4, 19, 5), + (9, 5, 1315, 4), + (10, 5, 1316, 4), + (11, 2, 623, 6), + (12, 2, 663, 6), + (13, 6, 624, 2), + (14, 6, 664, 2); +/*!40000 ALTER TABLE `rooms_change_points` ENABLE KEYS */; + +-- Dumping structure for table rooms_return_points +CREATE TABLE IF NOT EXISTS `rooms_return_points` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `room_id` int unsigned NOT NULL, + `direction` varchar(5) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `x` int unsigned NOT NULL, + `y` int unsigned NOT NULL, + `is_default` int unsigned NOT NULL, + `from_room_id` int unsigned DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `FK_scenes_return_points_rooms` (`room_id`), + KEY `FK_scenes_return_points_rooms_2` (`from_room_id`) USING BTREE, + CONSTRAINT `FK_rooms_return_points_rooms_from_room_id` FOREIGN KEY (`from_room_id`) REFERENCES `rooms` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT `FK_rooms_return_points_rooms_room_id` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table rooms_return_points: ~11 rows (approximately) +/*!40000 ALTER TABLE `rooms_return_points` DISABLE KEYS */; +INSERT INTO `rooms_return_points` (`id`, `room_id`, `direction`, `x`, `y`, `is_default`, `from_room_id`) VALUES + (1, 2, 'up', 548, 615, 0, 4), + (2, 3, 'up', 640, 600, 1, NULL), + (3, 4, 'down', 400, 345, 1, 2), + (4, 4, 'down', 1266, 670, 0, 3), + (5, 5, 'up', 640, 768, 0, 4), + (6, 5, 'up', 640, 768, 0, 4), + (7, 4, 'down', 615, 64, 0, 5), + (8, 4, 'down', 615, 64, 0, 5), + (9, 6, 'right', 820, 500, 0, 2), + (10, 6, 'right', 820, 500, 0, 2), + (11, 2, 'left', 720, 540, 0, 6); +/*!40000 ALTER TABLE `rooms_return_points` ENABLE KEYS */; + +-- Dumping structure for table skills_class_level_up_animations +CREATE TABLE IF NOT EXISTS `skills_class_level_up_animations` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `class_path_id` int unsigned DEFAULT NULL, + `level_id` int unsigned DEFAULT NULL, + `animationData` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `class_path_id_level_id` (`class_path_id`,`level_id`) USING BTREE, + KEY `FK_skills_class_level_up_skills_levels` (`level_id`) USING BTREE, + CONSTRAINT `FK_skills_class_level_up_skills_class_path` FOREIGN KEY (`class_path_id`) REFERENCES `skills_class_path` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT `FK_skills_class_level_up_skills_levels` FOREIGN KEY (`level_id`) REFERENCES `skills_levels` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_class_level_up_animations: ~1 rows (approximately) +/*!40000 ALTER TABLE `skills_class_level_up_animations` DISABLE KEYS */; +INSERT INTO `skills_class_level_up_animations` (`id`, `class_path_id`, `level_id`, `animationData`) VALUES + (1, NULL, NULL, '{"enabled":true,"type":"spritesheet","img":"heal_cast","frameWidth":64,"frameHeight":70,"start":0,"end":3,"repeat":-1,"destroyTime":2000,"depthByPlayer":"above"}'); +/*!40000 ALTER TABLE `skills_class_level_up_animations` ENABLE KEYS */; + +-- Dumping structure for table skills_class_path +CREATE TABLE IF NOT EXISTS `skills_class_path` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `levels_set_id` int unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key` (`key`), + KEY `levels_set_id` (`levels_set_id`), + CONSTRAINT `FK_skills_class_path_skills_levels_set` FOREIGN KEY (`levels_set_id`) REFERENCES `skills_levels_set` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_class_path: ~5 rows (approximately) +/*!40000 ALTER TABLE `skills_class_path` DISABLE KEYS */; +INSERT INTO `skills_class_path` (`id`, `key`, `label`, `levels_set_id`) VALUES + (1, 'journeyman', 'Journeyman', 1), + (2, 'sorcerer', 'Sorcerer', 2), + (3, 'warlock', 'Warlock', 3), + (4, 'swordsman', 'Swordsman', 4), + (5, 'warrior', 'Warrior', 5); +/*!40000 ALTER TABLE `skills_class_path` ENABLE KEYS */; + +-- Dumping structure for table skills_class_path_level_labels +CREATE TABLE IF NOT EXISTS `skills_class_path_level_labels` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `class_path_id` int unsigned NOT NULL, + `level_id` int unsigned NOT NULL, + `label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `class_path_id_level_key` (`class_path_id`,`level_id`) USING BTREE, + KEY `class_path_id` (`class_path_id`), + KEY `level_key` (`level_id`) USING BTREE, + CONSTRAINT `FK__skills_class_path` FOREIGN KEY (`class_path_id`) REFERENCES `skills_class_path` (`id`) ON UPDATE CASCADE, + CONSTRAINT `FK_skills_class_path_level_labels_skills_levels` FOREIGN KEY (`level_id`) REFERENCES `skills_levels` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_class_path_level_labels: ~5 rows (approximately) +/*!40000 ALTER TABLE `skills_class_path_level_labels` DISABLE KEYS */; +INSERT INTO `skills_class_path_level_labels` (`id`, `class_path_id`, `level_id`, `label`) VALUES + (1, 1, 3, 'Old Traveler'), + (2, 2, 7, 'Fire Master'), + (3, 3, 11, 'Magus'), + (4, 4, 15, 'Blade Master'), + (5, 5, 19, 'Paladin'); +/*!40000 ALTER TABLE `skills_class_path_level_labels` ENABLE KEYS */; + +-- Dumping structure for table skills_class_path_level_skills +CREATE TABLE IF NOT EXISTS `skills_class_path_level_skills` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `class_path_id` int unsigned NOT NULL, + `level_id` int unsigned NOT NULL, + `skill_id` int unsigned NOT NULL, + PRIMARY KEY (`id`), + KEY `class_path_id` (`class_path_id`), + KEY `skill_id` (`skill_id`), + KEY `level_key` (`level_id`) USING BTREE, + CONSTRAINT `FK_skills_class_path_level_skills_skills_class_path` FOREIGN KEY (`class_path_id`) REFERENCES `skills_class_path` (`id`) ON UPDATE CASCADE, + CONSTRAINT `FK_skills_class_path_level_skills_skills_levels` FOREIGN KEY (`level_id`) REFERENCES `skills_levels` (`key`) ON UPDATE CASCADE, + CONSTRAINT `FK_skills_class_path_level_skills_skills_levels_id` FOREIGN KEY (`level_id`) REFERENCES `skills_levels` (`id`) ON UPDATE CASCADE, + CONSTRAINT `FK_skills_class_path_level_skills_skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_class_path_level_skills: ~15 rows (approximately) +/*!40000 ALTER TABLE `skills_class_path_level_skills` DISABLE KEYS */; +INSERT INTO `skills_class_path_level_skills` (`id`, `class_path_id`, `level_id`, `skill_id`) VALUES + (1, 1, 1, 2), + (2, 1, 3, 1), + (3, 1, 4, 3), + (4, 1, 4, 4), + (5, 2, 5, 1), + (6, 2, 7, 3), + (7, 2, 8, 4), + (8, 3, 9, 1), + (9, 3, 11, 3), + (10, 3, 12, 2), + (11, 4, 13, 2), + (12, 4, 15, 4), + (13, 5, 17, 2), + (14, 5, 19, 1), + (15, 5, 20, 4); +/*!40000 ALTER TABLE `skills_class_path_level_skills` ENABLE KEYS */; + +-- Dumping structure for table skills_groups +CREATE TABLE IF NOT EXISTS `skills_groups` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `sort` int unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_groups: ~0 rows (approximately) +/*!40000 ALTER TABLE `skills_groups` DISABLE KEYS */; +/*!40000 ALTER TABLE `skills_groups` ENABLE KEYS */; + +-- Dumping structure for table skills_levels +CREATE TABLE IF NOT EXISTS `skills_levels` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `key` int unsigned NOT NULL, + `label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `required_experience` bigint unsigned DEFAULT NULL, + `level_set_id` int unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `key_level_set_id` (`key`,`level_set_id`), + KEY `level_set_id` (`level_set_id`), + CONSTRAINT `FK_skills_levels_skills_levels_set` FOREIGN KEY (`level_set_id`) REFERENCES `skills_levels_set` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_levels: ~20 rows (approximately) +/*!40000 ALTER TABLE `skills_levels` DISABLE KEYS */; +INSERT INTO `skills_levels` (`id`, `key`, `label`, `required_experience`, `level_set_id`) VALUES + (1, 1, '1', 0, 1), + (2, 2, '2', 100, 1), + (3, 5, '5', 338, 1), + (4, 10, '10', 2570, 1), + (5, 1, '1', 0, 2), + (6, 2, '2', 100, 2), + (7, 5, '5', 338, 2), + (8, 10, '10', 2570, 2), + (9, 1, '1', 0, 3), + (10, 2, '2', 100, 3), + (11, 5, '5', 338, 3), + (12, 10, '10', 2570, 3), + (13, 1, '1', 0, 4), + (14, 2, '2', 100, 4), + (15, 5, '5', 338, 4), + (16, 10, '10', 2570, 4), + (17, 1, '1', 0, 5), + (18, 2, '2', 100, 5), + (19, 5, '5', 338, 5), + (20, 10, '10', 2570, 5); +/*!40000 ALTER TABLE `skills_levels` ENABLE KEYS */; + +-- Dumping structure for table skills_levels_modifiers +CREATE TABLE IF NOT EXISTS `skills_levels_modifiers` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `level_id` int unsigned NOT NULL, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `operation` int unsigned NOT NULL, + `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `minValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `maxValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `minProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `maxProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `modifier_id` (`key`) USING BTREE, + KEY `level_key` (`level_id`) USING BTREE, + CONSTRAINT `FK_skills_levels_modifiers_skills_levels` FOREIGN KEY (`level_id`) REFERENCES `skills_levels` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=121 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='Modifiers table.'; + +-- Dumping data for table skills_levels_modifiers: ~120 rows (approximately) +/*!40000 ALTER TABLE `skills_levels_modifiers` DISABLE KEYS */; +INSERT INTO `skills_levels_modifiers` (`id`, `level_id`, `key`, `property_key`, `operation`, `value`, `minValue`, `maxValue`, `minProperty`, `maxProperty`) VALUES + (1, 2, 'inc_atk', 'stats/atk', 1, '10', NULL, NULL, NULL, NULL), + (2, 2, 'inc_def', 'stats/def', 1, '10', NULL, NULL, NULL, NULL), + (3, 2, 'inc_hp', 'stats/hp', 1, '10', NULL, NULL, NULL, NULL), + (4, 2, 'inc_mp', 'stats/mp', 1, '10', NULL, NULL, NULL, NULL), + (5, 2, 'inc_atk', 'statsBase/atk', 1, '10', NULL, NULL, NULL, NULL), + (6, 2, 'inc_def', 'statsBase/def', 1, '10', NULL, NULL, NULL, NULL), + (7, 2, 'inc_hp', 'statsBase/hp', 1, '10', NULL, NULL, NULL, NULL), + (8, 2, 'inc_mp', 'statsBase/mp', 1, '10', NULL, NULL, NULL, NULL), + (9, 3, 'inc_atk', 'stats/atk', 1, '20', NULL, NULL, NULL, NULL), + (10, 3, 'inc_def', 'stats/def', 1, '20', NULL, NULL, NULL, NULL), + (11, 3, 'inc_hp', 'stats/hp', 1, '20', NULL, NULL, NULL, NULL), + (12, 3, 'inc_mp', 'stats/mp', 1, '20', NULL, NULL, NULL, NULL), + (13, 3, 'inc_atk', 'statsBase/atk', 1, '20', NULL, NULL, NULL, NULL), + (14, 3, 'inc_def', 'statsBase/def', 1, '20', NULL, NULL, NULL, NULL), + (15, 3, 'inc_hp', 'statsBase/hp', 1, '20', NULL, NULL, NULL, NULL), + (16, 3, 'inc_mp', 'statsBase/mp', 1, '20', NULL, NULL, NULL, NULL), + (17, 4, 'inc_atk', 'stats/atk', 1, '50', NULL, NULL, NULL, NULL), + (18, 4, 'inc_def', 'stats/def', 1, '50', NULL, NULL, NULL, NULL), + (19, 4, 'inc_hp', 'stats/hp', 1, '50', NULL, NULL, NULL, NULL), + (20, 4, 'inc_mp', 'stats/mp', 1, '50', NULL, NULL, NULL, NULL), + (21, 4, 'inc_atk', 'statsBase/atk', 1, '50', NULL, NULL, NULL, NULL), + (22, 4, 'inc_def', 'statsBase/def', 1, '50', NULL, NULL, NULL, NULL), + (23, 4, 'inc_hp', 'statsBase/hp', 1, '50', NULL, NULL, NULL, NULL), + (24, 4, 'inc_mp', 'statsBase/mp', 1, '50', NULL, NULL, NULL, NULL), + (25, 6, 'inc_atk', 'stats/atk', 1, '10', NULL, NULL, NULL, NULL), + (26, 6, 'inc_def', 'stats/def', 1, '10', NULL, NULL, NULL, NULL), + (27, 6, 'inc_hp', 'stats/hp', 1, '10', NULL, NULL, NULL, NULL), + (28, 6, 'inc_mp', 'stats/mp', 1, '10', NULL, NULL, NULL, NULL), + (29, 6, 'inc_atk', 'statsBase/atk', 1, '10', NULL, NULL, NULL, NULL), + (30, 6, 'inc_def', 'statsBase/def', 1, '10', NULL, NULL, NULL, NULL), + (31, 6, 'inc_hp', 'statsBase/hp', 1, '10', NULL, NULL, NULL, NULL), + (32, 6, 'inc_mp', 'statsBase/mp', 1, '10', NULL, NULL, NULL, NULL), + (33, 7, 'inc_atk', 'stats/atk', 1, '20', NULL, NULL, NULL, NULL), + (34, 7, 'inc_def', 'stats/def', 1, '20', NULL, NULL, NULL, NULL), + (35, 7, 'inc_hp', 'stats/hp', 1, '20', NULL, NULL, NULL, NULL), + (36, 7, 'inc_mp', 'stats/mp', 1, '20', NULL, NULL, NULL, NULL), + (37, 7, 'inc_atk', 'statsBase/atk', 1, '20', NULL, NULL, NULL, NULL), + (38, 7, 'inc_def', 'statsBase/def', 1, '20', NULL, NULL, NULL, NULL), + (39, 7, 'inc_hp', 'statsBase/hp', 1, '20', NULL, NULL, NULL, NULL), + (40, 7, 'inc_mp', 'statsBase/mp', 1, '20', NULL, NULL, NULL, NULL), + (41, 8, 'inc_atk', 'stats/atk', 1, '50', NULL, NULL, NULL, NULL), + (42, 8, 'inc_def', 'stats/def', 1, '50', NULL, NULL, NULL, NULL), + (43, 8, 'inc_hp', 'stats/hp', 1, '50', NULL, NULL, NULL, NULL), + (44, 8, 'inc_mp', 'stats/mp', 1, '50', NULL, NULL, NULL, NULL), + (45, 8, 'inc_atk', 'statsBase/atk', 1, '50', NULL, NULL, NULL, NULL), + (46, 8, 'inc_def', 'statsBase/def', 1, '50', NULL, NULL, NULL, NULL), + (47, 8, 'inc_hp', 'statsBase/hp', 1, '50', NULL, NULL, NULL, NULL), + (48, 8, 'inc_mp', 'statsBase/mp', 1, '50', NULL, NULL, NULL, NULL), + (49, 10, 'inc_atk', 'stats/atk', 1, '10', NULL, NULL, NULL, NULL), + (50, 10, 'inc_def', 'stats/def', 1, '10', NULL, NULL, NULL, NULL), + (51, 10, 'inc_hp', 'stats/hp', 1, '10', NULL, NULL, NULL, NULL), + (52, 10, 'inc_mp', 'stats/mp', 1, '10', NULL, NULL, NULL, NULL), + (53, 10, 'inc_atk', 'statsBase/atk', 1, '10', NULL, NULL, NULL, NULL), + (54, 10, 'inc_def', 'statsBase/def', 1, '10', NULL, NULL, NULL, NULL), + (55, 10, 'inc_hp', 'statsBase/hp', 1, '10', NULL, NULL, NULL, NULL), + (56, 10, 'inc_mp', 'statsBase/mp', 1, '10', NULL, NULL, NULL, NULL), + (57, 11, 'inc_atk', 'stats/atk', 1, '20', NULL, NULL, NULL, NULL), + (58, 11, 'inc_def', 'stats/def', 1, '20', NULL, NULL, NULL, NULL), + (59, 11, 'inc_hp', 'stats/hp', 1, '20', NULL, NULL, NULL, NULL), + (60, 11, 'inc_mp', 'stats/mp', 1, '20', NULL, NULL, NULL, NULL), + (61, 11, 'inc_atk', 'statsBase/atk', 1, '20', NULL, NULL, NULL, NULL), + (62, 11, 'inc_def', 'statsBase/def', 1, '20', NULL, NULL, NULL, NULL), + (63, 11, 'inc_hp', 'statsBase/hp', 1, '20', NULL, NULL, NULL, NULL), + (64, 11, 'inc_mp', 'statsBase/mp', 1, '20', NULL, NULL, NULL, NULL), + (65, 12, 'inc_atk', 'stats/atk', 1, '50', NULL, NULL, NULL, NULL), + (66, 12, 'inc_def', 'stats/def', 1, '50', NULL, NULL, NULL, NULL), + (67, 12, 'inc_hp', 'stats/hp', 1, '50', NULL, NULL, NULL, NULL), + (68, 12, 'inc_mp', 'stats/mp', 1, '50', NULL, NULL, NULL, NULL), + (69, 12, 'inc_atk', 'statsBase/atk', 1, '50', NULL, NULL, NULL, NULL), + (70, 12, 'inc_def', 'statsBase/def', 1, '50', NULL, NULL, NULL, NULL), + (71, 12, 'inc_hp', 'statsBase/hp', 1, '50', NULL, NULL, NULL, NULL), + (72, 12, 'inc_mp', 'statsBase/mp', 1, '50', NULL, NULL, NULL, NULL), + (73, 14, 'inc_atk', 'stats/atk', 1, '10', NULL, NULL, NULL, NULL), + (74, 14, 'inc_def', 'stats/def', 1, '10', NULL, NULL, NULL, NULL), + (75, 14, 'inc_hp', 'stats/hp', 1, '10', NULL, NULL, NULL, NULL), + (76, 14, 'inc_mp', 'stats/mp', 1, '10', NULL, NULL, NULL, NULL), + (77, 14, 'inc_atk', 'statsBase/atk', 1, '10', NULL, NULL, NULL, NULL), + (78, 14, 'inc_def', 'statsBase/def', 1, '10', NULL, NULL, NULL, NULL), + (79, 14, 'inc_hp', 'statsBase/hp', 1, '10', NULL, NULL, NULL, NULL), + (80, 14, 'inc_mp', 'statsBase/mp', 1, '10', NULL, NULL, NULL, NULL), + (81, 15, 'inc_atk', 'stats/atk', 1, '20', NULL, NULL, NULL, NULL), + (82, 15, 'inc_def', 'stats/def', 1, '20', NULL, NULL, NULL, NULL), + (83, 15, 'inc_hp', 'stats/hp', 1, '20', NULL, NULL, NULL, NULL), + (84, 15, 'inc_mp', 'stats/mp', 1, '20', NULL, NULL, NULL, NULL), + (85, 15, 'inc_atk', 'statsBase/atk', 1, '20', NULL, NULL, NULL, NULL), + (86, 15, 'inc_def', 'statsBase/def', 1, '20', NULL, NULL, NULL, NULL), + (87, 15, 'inc_hp', 'statsBase/hp', 1, '20', NULL, NULL, NULL, NULL), + (88, 15, 'inc_mp', 'statsBase/mp', 1, '20', NULL, NULL, NULL, NULL), + (89, 16, 'inc_atk', 'stats/atk', 1, '50', NULL, NULL, NULL, NULL), + (90, 16, 'inc_def', 'stats/def', 1, '50', NULL, NULL, NULL, NULL), + (91, 16, 'inc_hp', 'stats/hp', 1, '50', NULL, NULL, NULL, NULL), + (92, 16, 'inc_mp', 'stats/mp', 1, '50', NULL, NULL, NULL, NULL), + (93, 16, 'inc_atk', 'statsBase/atk', 1, '50', NULL, NULL, NULL, NULL), + (94, 16, 'inc_def', 'statsBase/def', 1, '50', NULL, NULL, NULL, NULL), + (95, 16, 'inc_hp', 'statsBase/hp', 1, '50', NULL, NULL, NULL, NULL), + (96, 16, 'inc_mp', 'statsBase/mp', 1, '50', NULL, NULL, NULL, NULL), + (97, 18, 'inc_atk', 'stats/atk', 1, '10', NULL, NULL, NULL, NULL), + (98, 18, 'inc_def', 'stats/def', 1, '10', NULL, NULL, NULL, NULL), + (99, 18, 'inc_hp', 'stats/hp', 1, '10', NULL, NULL, NULL, NULL), + (100, 18, 'inc_mp', 'stats/mp', 1, '10', NULL, NULL, NULL, NULL), + (101, 18, 'inc_atk', 'statsBase/atk', 1, '10', NULL, NULL, NULL, NULL), + (102, 18, 'inc_def', 'statsBase/def', 1, '10', NULL, NULL, NULL, NULL), + (103, 18, 'inc_hp', 'statsBase/hp', 1, '10', NULL, NULL, NULL, NULL), + (104, 18, 'inc_mp', 'statsBase/mp', 1, '10', NULL, NULL, NULL, NULL), + (105, 19, 'inc_atk', 'stats/atk', 1, '20', NULL, NULL, NULL, NULL), + (106, 19, 'inc_def', 'stats/def', 1, '20', NULL, NULL, NULL, NULL), + (107, 19, 'inc_hp', 'stats/hp', 1, '20', NULL, NULL, NULL, NULL), + (108, 19, 'inc_mp', 'stats/mp', 1, '20', NULL, NULL, NULL, NULL), + (109, 19, 'inc_atk', 'statsBase/atk', 1, '20', NULL, NULL, NULL, NULL), + (110, 19, 'inc_def', 'statsBase/def', 1, '20', NULL, NULL, NULL, NULL), + (111, 19, 'inc_hp', 'statsBase/hp', 1, '20', NULL, NULL, NULL, NULL), + (112, 19, 'inc_mp', 'statsBase/mp', 1, '20', NULL, NULL, NULL, NULL), + (113, 20, 'inc_atk', 'stats/atk', 1, '50', NULL, NULL, NULL, NULL), + (114, 20, 'inc_def', 'stats/def', 1, '50', NULL, NULL, NULL, NULL), + (115, 20, 'inc_hp', 'stats/hp', 1, '50', NULL, NULL, NULL, NULL), + (116, 20, 'inc_mp', 'stats/mp', 1, '50', NULL, NULL, NULL, NULL), + (117, 20, 'inc_atk', 'statsBase/atk', 1, '50', NULL, NULL, NULL, NULL), + (118, 20, 'inc_def', 'statsBase/def', 1, '50', NULL, NULL, NULL, NULL), + (119, 20, 'inc_hp', 'statsBase/hp', 1, '50', NULL, NULL, NULL, NULL), + (120, 20, 'inc_mp', 'statsBase/mp', 1, '50', NULL, NULL, NULL, NULL); +/*!40000 ALTER TABLE `skills_levels_modifiers` ENABLE KEYS */; + +-- Dumping structure for table skills_levels_modifiers_conditions +CREATE TABLE IF NOT EXISTS `skills_levels_modifiers_conditions` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `levels_modifier_id` int unsigned NOT NULL, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `operation` varchar(50) CHARACTER SET utf32 COLLATE utf32_unicode_ci NOT NULL COMMENT 'eq,ne,lt,gt,le,ge', + `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + KEY `levels_modifier_id` (`levels_modifier_id`) USING BTREE, + CONSTRAINT `FK_skills_levels_modifiers_conditions_skills_levels_modifiers` FOREIGN KEY (`levels_modifier_id`) REFERENCES `skills_levels_modifiers` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf32 COLLATE=utf32_unicode_ci; + +-- Dumping data for table skills_levels_modifiers_conditions: ~0 rows (approximately) +/*!40000 ALTER TABLE `skills_levels_modifiers_conditions` DISABLE KEYS */; +/*!40000 ALTER TABLE `skills_levels_modifiers_conditions` ENABLE KEYS */; + +-- Dumping structure for table skills_levels_set +CREATE TABLE IF NOT EXISTS `skills_levels_set` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `autoFillRanges` int unsigned NOT NULL DEFAULT '0', + `autoFillExperienceMultiplier` int unsigned DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_levels_set: ~5 rows (approximately) +/*!40000 ALTER TABLE `skills_levels_set` DISABLE KEYS */; +INSERT INTO `skills_levels_set` (`id`, `autoFillRanges`, `autoFillExperienceMultiplier`) VALUES + (1, 1, NULL), + (2, 1, NULL), + (3, 1, NULL), + (4, 1, NULL), + (5, 1, NULL); +/*!40000 ALTER TABLE `skills_levels_set` ENABLE KEYS */; + +-- Dumping structure for table skills_owners_class_path +CREATE TABLE IF NOT EXISTS `skills_owners_class_path` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `class_path_id` int unsigned NOT NULL, + `owner_id` int unsigned NOT NULL, + `currentLevel` bigint unsigned NOT NULL DEFAULT '0', + `currentExp` bigint unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `level_set_id` (`class_path_id`) USING BTREE, + CONSTRAINT `FK_skills_owners_class_path_skills_class_path` FOREIGN KEY (`class_path_id`) REFERENCES `skills_class_path` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_owners_class_path: ~0 rows (approximately) +/*!40000 ALTER TABLE `skills_owners_class_path` DISABLE KEYS */; +/*!40000 ALTER TABLE `skills_owners_class_path` ENABLE KEYS */; + +-- Dumping structure for table skills_skill +CREATE TABLE IF NOT EXISTS `skills_skill` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'B: 1, ATK: 2, EFCT: 3, PHYS-ATK: 4, PHYS-EFCT: 5', + `autoValidation` int NOT NULL, + `skillDelay` int NOT NULL, + `castTime` int NOT NULL, + `usesLimit` int NOT NULL DEFAULT '0', + `range` int NOT NULL, + `rangeAutomaticValidation` int NOT NULL, + `rangePropertyX` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'Property path', + `rangePropertyY` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'Property path', + `rangeTargetPropertyX` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Target property path', + `rangeTargetPropertyY` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Target property path', + `allowSelfTarget` int NOT NULL, + `criticalChance` int DEFAULT NULL, + `criticalMultiplier` int DEFAULT NULL, + `criticalFixedValue` int DEFAULT NULL, + `customData` text CHARACTER SET utf8 COLLATE utf8_unicode_ci COMMENT 'Any custom data, recommended JSON format.', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `key` (`key`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_skill: ~4 rows (approximately) +/*!40000 ALTER TABLE `skills_skill` DISABLE KEYS */; +INSERT INTO `skills_skill` (`id`, `key`, `type`, `autoValidation`, `skillDelay`, `castTime`, `usesLimit`, `range`, `rangeAutomaticValidation`, `rangePropertyX`, `rangePropertyY`, `rangeTargetPropertyX`, `rangeTargetPropertyY`, `allowSelfTarget`, `criticalChance`, `criticalMultiplier`, `criticalFixedValue`, `customData`) VALUES + (1, 'attackBullet', '4', 0, 1000, 0, 0, 250, 1, 'state/x', 'state/y', NULL, NULL, 0, 10, 2, 0, NULL), + (2, 'attackShort', '2', 0, 600, 0, 0, 50, 1, 'state/x', 'state/y', NULL, NULL, 0, 10, 2, 0, NULL), + (3, 'fireball', '4', 0, 1500, 2000, 0, 280, 1, 'state/x', 'state/y', NULL, NULL, 0, 10, 2, 0, NULL), + (4, 'heal', '3', 0, 1500, 2000, 0, 100, 1, 'state/x', 'state/y', NULL, NULL, 1, 0, 1, 0, NULL); +/*!40000 ALTER TABLE `skills_skill` ENABLE KEYS */; + +-- Dumping structure for table skills_skill_animations +CREATE TABLE IF NOT EXISTS `skills_skill_animations` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `skill_id` int unsigned NOT NULL, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'Name conventions [key] + _atk, _cast, _bullet, _hit or _death.', + `classKey` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `animationData` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `skill_id_key` (`skill_id`,`key`) USING BTREE, + KEY `id` (`id`) USING BTREE, + KEY `key` (`key`) USING BTREE, + KEY `skill_id` (`skill_id`) USING BTREE, + CONSTRAINT `FK_skills_skill_animations_skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_skill_animations: ~4 rows (approximately) +/*!40000 ALTER TABLE `skills_skill_animations` DISABLE KEYS */; +INSERT INTO `skills_skill_animations` (`id`, `skill_id`, `key`, `classKey`, `animationData`) VALUES + (1, 3, 'bullet', NULL, '{"enabled":true,"type":"spritesheet","img":"fireball_bullet","frameWidth":64,"frameHeight":64,"start":0,"end":3,"repeat":-1,"rate":1,"dir":3}'), + (2, 3, 'cast', NULL, '{"enabled":true,"type":"spritesheet","img":"fireball_cast","frameWidth":64,"frameHeight":70,"start":0,"end":3,"repeat":-1,"destroyTime":2000,"depthByPlayer":"above"}'), + (3, 4, 'cast', NULL, '{"enabled":true,"type":"spritesheet","img":"heal_cast","frameWidth":64,"frameHeight":70,"start":0,"end":3,"repeat":-1,"destroyTime":2000}'), + (6, 4, 'hit', NULL, '{"enabled":true,"type":"spritesheet","img":"heal_hit","frameWidth":64,"frameHeight":70,"start":0,"end":4,"repeat":0,"depthByPlayer":"above"}'); +/*!40000 ALTER TABLE `skills_skill_animations` ENABLE KEYS */; + +-- Dumping structure for table skills_skill_attack +CREATE TABLE IF NOT EXISTS `skills_skill_attack` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `skill_id` int unsigned NOT NULL, + `affectedProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `allowEffectBelowZero` int unsigned NOT NULL DEFAULT '0', + `hitDamage` int unsigned NOT NULL, + `applyDirectDamage` int unsigned NOT NULL DEFAULT '0', + `attackProperties` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `defenseProperties` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `aimProperties` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `dodgeProperties` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `dodgeFullEnabled` int NOT NULL DEFAULT '1', + `dodgeOverAimSuccess` int NOT NULL DEFAULT '2', + `damageAffected` int NOT NULL DEFAULT '0', + `criticalAffected` int NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `skill_id` (`skill_id`), + CONSTRAINT `FK__skills_skill_attack` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_skill_attack: ~3 rows (approximately) +/*!40000 ALTER TABLE `skills_skill_attack` DISABLE KEYS */; +INSERT INTO `skills_skill_attack` (`id`, `skill_id`, `affectedProperty`, `allowEffectBelowZero`, `hitDamage`, `applyDirectDamage`, `attackProperties`, `defenseProperties`, `aimProperties`, `dodgeProperties`, `dodgeFullEnabled`, `dodgeOverAimSuccess`, `damageAffected`, `criticalAffected`) VALUES + (1, 1, 'stats/hp', 0, 3, 0, 'stats/atk,stats/stamina,stats/speed', 'stats/def,stats/stamina,stats/speed', 'stats/aim', 'stats/dodge', 1, 2, 0, 0), + (2, 2, 'stats/hp', 0, 5, 0, 'stats/atk,stats/stamina,stats/speed', 'stats/def,stats/stamina,stats/speed', 'stats/aim', 'stats/dodge', 1, 2, 0, 0), + (3, 3, 'stats/hp', 0, 7, 0, 'stats/atk,stats/stamina,stats/speed', 'stats/def,stats/stamina,stats/speed', 'stats/aim', 'stats/dodge', 1, 2, 0, 0); +/*!40000 ALTER TABLE `skills_skill_attack` ENABLE KEYS */; + +-- Dumping structure for table skills_skill_group_relation +CREATE TABLE IF NOT EXISTS `skills_skill_group_relation` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `skill_id` int unsigned NOT NULL, + `group_id` int unsigned NOT NULL, + PRIMARY KEY (`id`), + KEY `group_id` (`group_id`), + KEY `skill_id` (`skill_id`), + CONSTRAINT `FK__skills_groups` FOREIGN KEY (`group_id`) REFERENCES `skills_groups` (`id`) ON UPDATE CASCADE, + CONSTRAINT `FK__skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_skill_group_relation: ~0 rows (approximately) +/*!40000 ALTER TABLE `skills_skill_group_relation` DISABLE KEYS */; +/*!40000 ALTER TABLE `skills_skill_group_relation` ENABLE KEYS */; + +-- Dumping structure for table skills_skill_owner_conditions +CREATE TABLE IF NOT EXISTS `skills_skill_owner_conditions` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `skill_id` int unsigned NOT NULL, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `conditional` varchar(50) CHARACTER SET utf32 COLLATE utf32_unicode_ci NOT NULL COMMENT 'eq,ne,lt,gt,le,ge', + `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + KEY `skill_id` (`skill_id`) USING BTREE, + CONSTRAINT `FK_skills_skill_owner_conditions_skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf32 COLLATE=utf32_unicode_ci; + +-- Dumping data for table skills_skill_owner_conditions: ~1 rows (approximately) +/*!40000 ALTER TABLE `skills_skill_owner_conditions` DISABLE KEYS */; +INSERT INTO `skills_skill_owner_conditions` (`id`, `skill_id`, `key`, `property_key`, `conditional`, `value`) VALUES + (1, 3, 'available_mp', 'stats/mp', 'ge', '5'); +/*!40000 ALTER TABLE `skills_skill_owner_conditions` ENABLE KEYS */; + +-- Dumping structure for table skills_skill_owner_effects +CREATE TABLE IF NOT EXISTS `skills_skill_owner_effects` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `skill_id` int unsigned NOT NULL, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `operation` int NOT NULL, + `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `minValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `maxValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `minProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `maxProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + KEY `skill_id` (`skill_id`) USING BTREE, + CONSTRAINT `FK_skills_skill_owner_effects_skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='Modifiers table.'; + +-- Dumping data for table skills_skill_owner_effects: ~1 rows (approximately) +/*!40000 ALTER TABLE `skills_skill_owner_effects` DISABLE KEYS */; +INSERT INTO `skills_skill_owner_effects` (`id`, `skill_id`, `key`, `property_key`, `operation`, `value`, `minValue`, `maxValue`, `minProperty`, `maxProperty`) VALUES + (2, 3, 'dec_mp', 'stats/mp', 2, '5', '0', '', NULL, NULL); +/*!40000 ALTER TABLE `skills_skill_owner_effects` ENABLE KEYS */; + +-- Dumping structure for table skills_skill_owner_effects_conditions +CREATE TABLE IF NOT EXISTS `skills_skill_owner_effects_conditions` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `skill_owner_effect_id` int unsigned NOT NULL, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `operation` varchar(50) CHARACTER SET utf32 COLLATE utf32_unicode_ci NOT NULL COMMENT 'eq,ne,lt,gt,le,ge', + `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + KEY `skill_owner_effect_id` (`skill_owner_effect_id`) USING BTREE, + CONSTRAINT `FK_skills_skill_owner_effects_conditions_skill_owner_effects` FOREIGN KEY (`skill_owner_effect_id`) REFERENCES `skills_skill_owner_effects` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf32 COLLATE=utf32_unicode_ci; + +-- Dumping data for table skills_skill_owner_effects_conditions: ~0 rows (approximately) +/*!40000 ALTER TABLE `skills_skill_owner_effects_conditions` DISABLE KEYS */; +/*!40000 ALTER TABLE `skills_skill_owner_effects_conditions` ENABLE KEYS */; + +-- Dumping structure for table skills_skill_physical_data +CREATE TABLE IF NOT EXISTS `skills_skill_physical_data` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `skill_id` int unsigned NOT NULL, + `magnitude` int unsigned NOT NULL, + `objectWidth` int unsigned NOT NULL, + `objectHeight` int unsigned NOT NULL, + `validateTargetOnHit` int unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `attack_skill_id` (`skill_id`) USING BTREE, + CONSTRAINT `FK_skills_skill_physical_data_skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table skills_skill_physical_data: ~2 rows (approximately) +/*!40000 ALTER TABLE `skills_skill_physical_data` DISABLE KEYS */; +INSERT INTO `skills_skill_physical_data` (`id`, `skill_id`, `magnitude`, `objectWidth`, `objectHeight`, `validateTargetOnHit`) VALUES + (1, 1, 350, 5, 5, 0), + (2, 3, 550, 5, 5, 0); +/*!40000 ALTER TABLE `skills_skill_physical_data` ENABLE KEYS */; + +-- Dumping structure for table skills_skill_target_effects +CREATE TABLE IF NOT EXISTS `skills_skill_target_effects` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `skill_id` int unsigned NOT NULL, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `operation` int unsigned NOT NULL, + `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `minValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `maxValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `minProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `maxProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + KEY `skill_id` (`skill_id`) USING BTREE, + CONSTRAINT `FK_skills_skill_effect_modifiers` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='Modifiers table.'; + +-- Dumping data for table skills_skill_target_effects: ~1 rows (approximately) +/*!40000 ALTER TABLE `skills_skill_target_effects` DISABLE KEYS */; +INSERT INTO `skills_skill_target_effects` (`id`, `skill_id`, `key`, `property_key`, `operation`, `value`, `minValue`, `maxValue`, `minProperty`, `maxProperty`) VALUES + (1, 4, 'heal', 'stats/hp', 1, '10', '0', '0', NULL, 'statsBase/hp'); +/*!40000 ALTER TABLE `skills_skill_target_effects` ENABLE KEYS */; + +-- Dumping structure for table skills_skill_target_effects_conditions +CREATE TABLE IF NOT EXISTS `skills_skill_target_effects_conditions` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `skill_target_effect_id` int unsigned NOT NULL, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `operation` varchar(50) CHARACTER SET utf32 COLLATE utf32_unicode_ci NOT NULL COMMENT 'eq,ne,lt,gt,le,ge', + `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + KEY `skill_target_effect_id` (`skill_target_effect_id`) USING BTREE, + CONSTRAINT `FK_skills_skill_target_effects_conditions_skill_target_effects` FOREIGN KEY (`skill_target_effect_id`) REFERENCES `skills_skill_target_effects` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf32 COLLATE=utf32_unicode_ci; + +-- Dumping data for table skills_skill_target_effects_conditions: ~0 rows (approximately) +/*!40000 ALTER TABLE `skills_skill_target_effects_conditions` DISABLE KEYS */; +/*!40000 ALTER TABLE `skills_skill_target_effects_conditions` ENABLE KEYS */; + +-- Dumping structure for table stats +CREATE TABLE IF NOT EXISTS `stats` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `base_value` int unsigned NOT NULL, + `customData` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `key` (`key`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + +-- Dumping data for table stats: ~10 rows (approximately) +/*!40000 ALTER TABLE `stats` DISABLE KEYS */; +INSERT INTO `stats` (`id`, `key`, `label`, `description`, `base_value`, `customData`) VALUES + (1, 'hp', 'HP', 'Player life points', 100, '{"showBase":true}'), + (2, 'mp', 'MP', 'Player magic points', 100, '{"showBase":true}'), + (3, 'atk', 'Atk', 'Player attack points', 100, NULL), + (4, 'def', 'Def', 'Player defense points', 100, NULL), + (5, 'dodge', 'Dodge', 'Player dodge points', 100, NULL), + (6, 'speed', 'Speed', 'Player speed point', 100, NULL), + (7, 'aim', 'Aim', 'Player aim points', 100, NULL), + (8, 'stamina', 'Stamina', 'Player stamina points', 100, '{"showBase":true}'), + (9, 'mgk-atk', 'Magic Atk', 'Player magic attack', 100, NULL), + (10, 'mgk-def', 'Magic Def', 'Player magic defense', 100, NULL); +/*!40000 ALTER TABLE `stats` ENABLE KEYS */; + +-- Dumping structure for table users +CREATE TABLE IF NOT EXISTS `users` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `email` varchar(255) NOT NULL, + `username` varchar(255) NOT NULL, + `password` varchar(255) NOT NULL, + `role_id` int unsigned NOT NULL, + `status` varchar(255) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `email` (`email`), + UNIQUE KEY `username` (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- Dumping data for table users: ~0 rows (approximately) +/*!40000 ALTER TABLE `users` DISABLE KEYS */; +/*!40000 ALTER TABLE `users` ENABLE KEYS */; + +/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; +/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */; diff --git a/migrations/production/beta.26-sql-update.sql b/migrations/production/beta.26-sql-update.sql new file mode 100644 index 000000000..747bad674 --- /dev/null +++ b/migrations/production/beta.26-sql-update.sql @@ -0,0 +1,226 @@ +####################################################################################################################### + +SET FOREIGN_KEY_CHECKS = 0; + +####################################################################################################################### + +# Config Types: +CREATE TABLE `config_types` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `label` VARCHAR(50) NOT NULL DEFAULT '0' COLLATE 'utf8_unicode_ci', + PRIMARY KEY (`id`) USING BTREE +) COLLATE='utf8_unicode_ci' ENGINE=InnoDB; + +INSERT INTO `config_types` VALUES (1, 'string'); +INSERT INTO `config_types` VALUES (2, 'float'); +INSERT INTO `config_types` VALUES (3, 'boolean'); +INSERT INTO `config_types` VALUES (4, 'json'); +INSERT INTO `config_types` VALUES (5, 'comma_separated'); + +UPDATE `config` SET `type` = 't' WHERE `path` = 'actions/pvp/timerType'; + +SET @string_id = (SELECT `id` FROM `config_types` WHERE `label` = 'string'); +SET @boolean_id = (SELECT `id` FROM `config_types` WHERE `label` = 'boolean'); +SET @float_id = (SELECT `id` FROM `config_types` WHERE `label` = 'float'); +SET @json_id = (SELECT `id` FROM `config_types` WHERE `label` = 'json'); +SET @comma_separated_id = (SELECT `id` FROM `config_types` WHERE `label` = 'comma_separated'); + +UPDATE `config` SET `type` = @string_id WHERE `type` = 't'; +UPDATE `config` SET `type` = @boolean_id WHERE `type` = 'b'; +UPDATE `config` SET `type` = @float_id WHERE `type` = 'i'; +UPDATE `config` SET `type` = @json_id WHERE `type` = 'j'; +UPDATE `config` SET `type` = @comma_separated_id WHERE `type` = 'c'; + +ALTER TABLE `config` CHANGE COLUMN `type` `type` INT UNSIGNED NOT NULL COLLATE 'utf8_unicode_ci' AFTER `value`; +ALTER TABLE `config` ADD CONSTRAINT `FK_config_config_types` FOREIGN KEY (`type`) REFERENCES `config_types` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION; +ALTER TABLE `config` ADD UNIQUE INDEX `scope_path` (`scope`, `path`); + +# Config: +INSERT INTO `config` VALUES (NULL, 'client', 'general/gameEngine/updateGameSizeTimeOut', '500', @float_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/options/acceptOrDecline', '{"1":{"label":"Accept","value":1},"2":{"label":"Decline","value":2}}', @json_id); +INSERT INTO `config` VALUES (NULL, 'client', 'team/labels/requestFromTitle', 'Team request from:', @string_id); +INSERT INTO `config` VALUES (NULL, 'client', 'team/labels/leaderNameTitle', 'Team leader: %leaderName', @string_id); +INSERT INTO `config` VALUES (NULL, 'client', 'team/labels/propertyMaxValue', '/ %propertyMaxValue', @string_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/teams/enabled', '1', @boolean_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/teams/responsiveX', '100', @float_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/teams/responsiveY', '0', @float_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/teams/x', '430', @float_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/teams/y', '100', @float_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/teams/sharedProperties', '{"hp":{"path":"stats/hp","pathMax":"statsBase/hp","label":"HP"},"mp":{"path":"stats/mp","pathMax":"statsBase/mp","label":"MP"}}', @json_id); +INSERT INTO `config` VALUES (NULL, 'client', 'clan/general/openInvites', '0', @boolean_id); +INSERT INTO `config` VALUES (NULL, 'client', 'clan/labels/requestFromTitle', 'Clan request from:', @string_id); +INSERT INTO `config` VALUES (NULL, 'client', 'clan/labels/clanTitle', 'Clan: %clanName - Leader: %leaderName', @string_id); +INSERT INTO `config` VALUES (NULL, 'client', 'clan/labels/propertyMaxValue', '/ %propertyMaxValue', @string_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/clan/enabled', '1', @boolean_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/clan/responsiveX', '100', @float_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/clan/responsiveY', '0', @float_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/clan/x', '430', @float_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/clan/y', '100', @float_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/clan/sharedProperties', '{"hp":{"path":"stats/hp","pathMax":"statsBase/hp","label":"HP"},"mp":{"path":"stats/mp","pathMax":"statsBase/mp","label":"MP"}}', @json_id); +INSERT INTO `config` VALUES (NULL, 'client', 'ui/controls/allowPrimaryTouch', '1', @boolean_id); +INSERT INTO `config` VALUES (NULL, 'server', 'rewards/actions/interactionsDistance', '40', @float_id); +INSERT INTO `config` VALUES (NULL, 'server', 'rewards/actions/disappearTime', '1800000', @float_id); +INSERT INTO `config` VALUES (NULL, 'client', 'rewards/titles/rewardMessage', 'You obtained %dropQuantity %itemLabel', @string_id); +INSERT INTO `config` VALUES (NULL, 'client', 'login/termsAndConditions/link', 'Accept our Terms and Conditions (click here).', @string_id); +INSERT INTO `config` VALUES (NULL, 'client', 'login/termsAndConditions/heading', 'Terms and conditions', @string_id); +INSERT INTO `config` VALUES (NULL, 'client', 'login/termsAndConditions/body', 'This is our test terms and conditions content.', @string_id); +INSERT INTO `config` VALUES (NULL, 'client', 'login/termsAndConditions/checkboxLabel', 'Accept terms and conditions', @string_id); +UPDATE `config` SET `value` = 'Played time %playedTimeInHs hs' WHERE `path` = 'players/playedTime/label'; + +# Features: +INSERT INTO `features` VALUES (NULL, 'teams', 'Teams', 1); +INSERT INTO `features` VALUES (NULL, 'rewards', 'Rewards', 1); + +# Rooms return positions fix: +DELETE FROM `rooms_return_points` WHERE `id` = 6 OR `id` = 8 OR `id` = 10; +UPDATE `rooms_return_points` SET `is_default` = 1 WHERE `id` = 1; +UPDATE `rooms_return_points` SET `from_room_id` = 4 WHERE `id` = 2; +UPDATE `rooms_return_points` SET `from_room_id` = 2 WHERE `id` = 3; +UPDATE `rooms_return_points` SET `from_room_id` = 3 WHERE `id` = 4; +UPDATE `rooms_return_points` SET `from_room_id` = 5 WHERE `id` = 7; + +# Clan and members: +CREATE TABLE `clan_levels` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `key` INT(10) UNSIGNED NOT NULL, + `label` VARCHAR(255) NOT NULL COLLATE 'utf8_unicode_ci', + `required_experience` BIGINT(20) UNSIGNED NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `key` (`key`) USING BTREE +) COLLATE='utf8_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=1; + +INSERT INTO `clan_levels` VALUES (NULL, 1, '1', 0); + +CREATE TABLE `clan` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `owner_id` INT(10) UNSIGNED NOT NULL, + `name` VARCHAR(50) NOT NULL DEFAULT '' COLLATE 'utf8_unicode_ci', + `points` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `level` INT(10) UNSIGNED NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `owner_id` (`owner_id`) USING BTREE, + UNIQUE INDEX `name` (`name`) USING BTREE, + INDEX `FK_clan_clan_levels` (`level`) USING BTREE, + CONSTRAINT `FK_clan_clan_levels` FOREIGN KEY (`level`) REFERENCES `clan_levels` (`key`) ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT `FK_clan_players` FOREIGN KEY (`owner_id`) REFERENCES `players` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION +) COLLATE='utf8_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=1; + +CREATE TABLE `clan_members` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `clan_id` INT(10) UNSIGNED NOT NULL, + `player_id` INT(10) UNSIGNED NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `clan_id_player_id` (`clan_id`, `player_id`) USING BTREE, + UNIQUE INDEX `player_id` (`player_id`) USING BTREE, + INDEX `FK__clan` (`clan_id`) USING BTREE, + INDEX `FK__players` (`player_id`) USING BTREE, + CONSTRAINT `FK_clan_members_clan` FOREIGN KEY (`clan_id`) REFERENCES `clan` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT `FK_clan_members_players` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION +) COLLATE='utf8_unicode_ci' ENGINE=InnoDB; + +CREATE TABLE `clan_levels_modifiers` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `level_id` INT(10) UNSIGNED NOT NULL, + `key` VARCHAR(255) NOT NULL COLLATE 'utf8_unicode_ci', + `property_key` VARCHAR(255) NOT NULL COLLATE 'utf8_unicode_ci', + `operation` INT(10) UNSIGNED NOT NULL, + `value` VARCHAR(255) NOT NULL COLLATE 'utf8_unicode_ci', + `minValue` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci', + `maxValue` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci', + `minProperty` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci', + `maxProperty` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci', + PRIMARY KEY (`id`) USING BTREE, + INDEX `modifier_id` (`key`) USING BTREE, + INDEX `level_key` (`level_id`) USING BTREE, + INDEX `FK_clan_levels_modifiers_operation_types` (`operation`) USING BTREE, + CONSTRAINT `FK_clan_levels_modifiers_operation_types` FOREIGN KEY (`operation`) REFERENCES `operation_types` (`key`) ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT `FK_clan_levels_modifiers_clan_levels` FOREIGN KEY (`level_id`) REFERENCES `clan_levels` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION +) COLLATE='utf8_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=1; + +# Inventory tables fix: +ALTER TABLE `items_inventory` DROP FOREIGN KEY `FK_items_inventory_items_item`; +ALTER TABLE `items_item` DROP FOREIGN KEY `FK_items_item_items_group`; +ALTER TABLE `items_item_modifiers` DROP FOREIGN KEY `FK_items_item_modifiers_items_item`; +ALTER TABLE `objects_items_inventory` DROP FOREIGN KEY `objects_items_inventory_ibfk_1`; + +ALTER TABLE `items_group` + CHANGE COLUMN `id` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST; +ALTER TABLE `items_inventory` + CHANGE COLUMN `id` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST, + CHANGE COLUMN `owner_id` `owner_id` INT(10) UNSIGNED NOT NULL AFTER `id`, + CHANGE COLUMN `item_id` `item_id` INT(10) UNSIGNED NOT NULL AFTER `owner_id`; +ALTER TABLE `items_item` + CHANGE COLUMN `id` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST, + CHANGE COLUMN `group_id` `group_id` INT(10) UNSIGNED NULL DEFAULT NULL AFTER `type`; +ALTER TABLE `items_item_modifiers` + CHANGE COLUMN `id` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST, + CHANGE COLUMN `item_id` `item_id` INT(10) UNSIGNED NOT NULL AFTER `id`; +ALTER TABLE `objects_items_inventory` + CHANGE COLUMN `item_id` `item_id` INT(10) UNSIGNED NOT NULL AFTER `owner_id`; +ALTER TABLE `features` + CHANGE COLUMN `is_enabled` `is_enabled` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0 AFTER `title`; + +ALTER TABLE `items_inventory` ADD CONSTRAINT `FK_items_inventory_items_item` FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION; +ALTER TABLE `items_item` ADD CONSTRAINT `FK_items_item_items_group` FOREIGN KEY (`group_id`) REFERENCES `items_group` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION; +ALTER TABLE `items_item_modifiers` ADD CONSTRAINT `FK_items_item_modifiers_items_item` FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION; +ALTER TABLE `objects_items_inventory` ADD CONSTRAINT `FK_objects_items_inventory_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION; + +# Rewards: +CREATE TABLE `rewards_modifiers` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `operation` int unsigned NOT NULL, + `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `minValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `maxValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `minProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `maxProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `modifier_id` (`key`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE `rewards` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `object_id` INT(10) UNSIGNED NOT NULL, + `item_id` INT(10) UNSIGNED NULL DEFAULT NULL, + `modifier_id` INT(10) UNSIGNED NULL DEFAULT NULL, + `experience` INT(11) UNSIGNED NOT NULL DEFAULT 0, + `drop_rate` INT(10) UNSIGNED NOT NULL, + `drop_quantity` INT(10) UNSIGNED NOT NULL, + `is_unique` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0, + `was_given` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0, + `has_drop_body` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_rewards_items_item` (`item_id`) USING BTREE, + INDEX `FK_rewards_objects` (`object_id`) USING BTREE, + CONSTRAINT `FK_rewards_items_item` FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT `FK_rewards_rewards_modifiers` FOREIGN KEY (`modifier_id`) REFERENCES `rewards_modifiers` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT `FK_rewards_objects` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION +) COLLATE='utf8_unicode_ci' ENGINE=InnoDB; + +CREATE TABLE `objects_items_rewards_animations` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `reward_id` INT(10) UNSIGNED NOT NULL, + `asset_type` varchar(255) NOT NULL, + `asset_key` varchar(255) NOT NULL, + `file` varchar(255) NOT NULL, + `extra_params` TEXT NULL, + PRIMARY KEY (`id`) USING BTREE, + INDEX `FK_objects_items_rewards_animations_rewards` (`reward_id`) USING BTREE, + CONSTRAINT `FK_objects_items_rewards_animations_rewards` FOREIGN KEY (`reward_id`) REFERENCES `rewards` (`id`) ON UPDATE CASCADE ON DELETE NO ACTION +) COLLATE='utf8_unicode_ci' ENGINE=InnoDB; + +INSERT INTO `rewards` VALUES + (1, 7, 2, null, 10, 100, 1, 0, 0, 1), + (2, 6, 2, null, 10, 100, 3, 0, 0, 1); + +INSERT INTO `objects_items_rewards_animations` VALUES + (1, 2, 'spritesheet', 'branch-sprite', 'branch-sprite', '{"start":0,"end":2,"repeat":-1,"frameWidth":32, "frameHeight":32,"depthByPlayer":"above"}'), + (2, 1, 'spritesheet', 'branch-sprite', 'branch-sprite', '{"start":0,"end":2,"repeat":-1,"frameWidth":32, "frameHeight":32,"depthByPlayer":"above"}'); + +####################################################################################################################### + +SET FOREIGN_KEY_CHECKS = 1; + +####################################################################################################################### diff --git a/migrations/production/reldens-install-v4.0.0.sql b/migrations/production/reldens-install-v4.0.0.sql index 053df2c37..08a715847 100644 --- a/migrations/production/reldens-install-v4.0.0.sql +++ b/migrations/production/reldens-install-v4.0.0.sql @@ -1,5 +1,6 @@ -- -------------------------------------------------------- --- Server version: 8.0.27 - MySQL Community Server - GPL +-- Host: 127.0.0.1 +-- Server version: 8.0.31 - MySQL Community Server - GPL -- Server OS: Win64 -- -------------------------------------------------------- @@ -27,9 +28,9 @@ CREATE TABLE IF NOT EXISTS `audio` ( KEY `FK_audio_audio_categories` (`category_id`), CONSTRAINT `FK_audio_audio_categories` FOREIGN KEY (`category_id`) REFERENCES `audio_categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_audio_rooms` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) ON DELETE SET NULL ON UPDATE SET NULL -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; --- Dumping data for table audio: ~3 rows (approximately) +-- Dumping data for table audio: ~2 rows (approximately) INSERT INTO `audio` (`id`, `audio_key`, `files_name`, `config`, `room_id`, `category_id`, `enabled`) VALUES (3, 'footstep', 'footstep.mp3', NULL, NULL, 3, 1), (4, 'ReldensTownAudio', 'reldens-town.mp3', '', 4, 1, 1); @@ -44,7 +45,7 @@ CREATE TABLE IF NOT EXISTS `audio_categories` ( PRIMARY KEY (`id`), UNIQUE KEY `category_key` (`category_key`), UNIQUE KEY `category_label` (`category_label`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table audio_categories: ~2 rows (approximately) INSERT INTO `audio_categories` (`id`, `category_key`, `category_label`, `enabled`, `single_audio`) VALUES @@ -63,7 +64,7 @@ CREATE TABLE IF NOT EXISTS `audio_markers` ( UNIQUE KEY `audio_id_marker_key` (`audio_id`,`marker_key`), KEY `audio_id` (`audio_id`), CONSTRAINT `FK_audio_markers_audio` FOREIGN KEY (`audio_id`) REFERENCES `audio` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table audio_markers: ~41 rows (approximately) INSERT INTO `audio_markers` (`id`, `audio_id`, `marker_key`, `start`, `duration`, `config`) VALUES @@ -120,7 +121,7 @@ CREATE TABLE IF NOT EXISTS `audio_player_config` ( KEY `FK_audio_player_config_audio_categories` (`category_id`), CONSTRAINT `FK_audio_player_config_audio_categories` FOREIGN KEY (`category_id`) REFERENCES `audio_categories` (`id`) ON UPDATE CASCADE, CONSTRAINT `FK_audio_player_config_players` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping structure for table chat CREATE TABLE IF NOT EXISTS `chat` ( @@ -138,7 +139,70 @@ CREATE TABLE IF NOT EXISTS `chat` ( CONSTRAINT `FK__players` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`), CONSTRAINT `FK__players_2` FOREIGN KEY (`private_player_id`) REFERENCES `players` (`id`), CONSTRAINT `FK__scenes` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=112 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- Dumping structure for table clan +CREATE TABLE IF NOT EXISTS `clan` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `owner_id` int unsigned NOT NULL, + `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '', + `points` int unsigned NOT NULL DEFAULT '0', + `level` int unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `owner_id` (`owner_id`), + UNIQUE KEY `name` (`name`), + KEY `FK_clan_clan_levels` (`level`), + CONSTRAINT `FK_clan_clan_levels` FOREIGN KEY (`level`) REFERENCES `clan_levels` (`key`), + CONSTRAINT `FK_clan_players` FOREIGN KEY (`owner_id`) REFERENCES `players` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=81 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- Dumping structure for table clan_levels +CREATE TABLE IF NOT EXISTS `clan_levels` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `key` int unsigned NOT NULL, + `label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `required_experience` bigint unsigned DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `key` (`key`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- Dumping data for table clan_levels: ~1 rows (approximately) +INSERT INTO `clan_levels` (`id`, `key`, `label`, `required_experience`) VALUES + (1, 1, '1', 0); + +-- Dumping structure for table clan_levels_modifiers +CREATE TABLE IF NOT EXISTS `clan_levels_modifiers` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `level_id` int unsigned NOT NULL, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `operation` int unsigned NOT NULL, + `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `minValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `maxValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `minProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `maxProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + KEY `modifier_id` (`key`) USING BTREE, + KEY `level_key` (`level_id`) USING BTREE, + KEY `FK_clan_levels_modifiers_operation_types` (`operation`) USING BTREE, + CONSTRAINT `FK_clan_levels_modifiers_clan_levels` FOREIGN KEY (`level_id`) REFERENCES `clan_levels` (`id`) ON UPDATE CASCADE, + CONSTRAINT `FK_clan_levels_modifiers_operation_types` FOREIGN KEY (`operation`) REFERENCES `operation_types` (`key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- Dumping structure for table clan_members +CREATE TABLE IF NOT EXISTS `clan_members` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `clan_id` int unsigned NOT NULL, + `player_id` int unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `clan_id_player_id` (`clan_id`,`player_id`), + UNIQUE KEY `player_id` (`player_id`), + KEY `FK__clan` (`clan_id`), + KEY `FK__players` (`player_id`), + CONSTRAINT `FK_clan_members_clan` FOREIGN KEY (`clan_id`) REFERENCES `clan` (`id`), + CONSTRAINT `FK_clan_members_players` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=97 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping structure for table config CREATE TABLE IF NOT EXISTS `config` ( @@ -146,278 +210,323 @@ CREATE TABLE IF NOT EXISTS `config` ( `scope` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `path` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `value` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, - `type` varchar(2) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=283 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; + `type` int unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `scope_path` (`scope`,`path`), + KEY `FK_config_config_types` (`type`), + CONSTRAINT `FK_config_config_types` FOREIGN KEY (`type`) REFERENCES `config_types` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=315 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; --- Dumping data for table config: ~236 rows (approximately) +-- Dumping data for table config: ~282 rows (approximately) INSERT INTO `config` (`id`, `scope`, `path`, `value`, `type`) VALUES - (1, 'server', 'rooms/validation/valid', 'room_game,chat_global', 't'), - (2, 'server', 'players/initialState/room_id', '4', 'i'), - (3, 'server', 'players/initialState/x', '400', 'i'), - (4, 'server', 'players/initialState/y', '345', 'i'), - (5, 'server', 'players/initialState/dir', 'down', 't'), - (13, 'server', 'rooms/validation/enabled', '1', 'b'), - (18, 'client', 'general/controls/allowSimultaneousKeys', '1', 'b'), - (19, 'server', 'rooms/world/timeStep', '0.04', 'i'), - (20, 'server', 'chat/messages/broadcast_join', '1', 'b'), - (21, 'server', 'chat/messages/broadcast_leave', '1', 'b'), - (22, 'server', 'chat/messages/global_enabled', '1', 'b'), - (23, 'server', 'chat/messages/global_allowed_roles', '1,9000', 't'), - (24, 'server', 'players/physicsBody/speed', '180', 'i'), - (25, 'client', 'players/animations/fadeDuration', '1000', 'i'), - (26, 'client', 'ui/playerBox/x', '50', 'i'), - (27, 'client', 'ui/playerStats/enabled', '1', 'b'), - (28, 'client', 'ui/controls/enabled', '1', 'b'), - (29, 'client', 'map/tileData/width', '16', 'i'), - (30, 'client', 'map/tileData/height', '16', 'i'), - (31, 'client', 'map/tileData/margin', '1', 'i'), - (32, 'client', 'map/tileData/spacing', '2', 'i'), - (33, 'client', 'players/size/width', '52', 'i'), - (34, 'client', 'players/size/height', '71', 'i'), - (35, 'client', 'general/animations/frameRate', '10', 'i'), - (36, 'client', 'map/layersDepth/belowPlayer', '0', 'i'), - (37, 'client', 'map/layersDepth/changePoints', '0', 'i'), - (38, 'client', 'ui/sceneLabel/enabled', '1', 'b'), - (39, 'client', 'general/controls/action_button_hold', '0', 'b'), - (40, 'client', 'ui/chat/x', '440', 'i'), - (41, 'client', 'ui/chat/y', '940', 'i'), - (42, 'server', 'players/actions/interactionDistance', '40', 'i'), - (43, 'server', 'objects/actions/interactionsDistance', '140', 'i'), - (44, 'client', 'ui/playerBox/enabled', '1', 'b'), - (45, 'client', 'ui/playerBox/y', '30', 'i'), - (46, 'client', 'ui/lifeBar/enabled', '1', 'b'), - (47, 'client', 'ui/uiTarget/x', '10', 'i'), - (48, 'client', 'ui/uiTarget/y', '85', 'i'), - (49, 'client', 'ui/sceneLabel/x', '250', 'i'), - (50, 'client', 'ui/sceneLabel/y', '20', 'i'), - (51, 'client', 'ui/controls/x', '120', 'i'), - (52, 'client', 'ui/controls/y', '390', 'i'), - (53, 'client', 'ui/playerStats/x', '430', 'i'), - (54, 'client', 'ui/playerStats/y', '20', 'i'), - (55, 'client', 'ui/loading/font', 'Verdana, Geneva, sans-serif', 't'), - (56, 'client', 'ui/loading/fontSize', '20px', 't'), - (57, 'client', 'ui/loading/assetsSize', '18px', 't'), - (58, 'client', 'ui/loading/loadingColor', '#ffffff', 't'), - (59, 'client', 'ui/loading/percentColor', '#666666', 't'), - (60, 'client', 'ui/loading/assetsColor', '#ffffff', 't'), - (61, 'client', 'ui/loading/showAssets', '1', 'b'), - (62, 'client', 'players/animations/basedOnPress', '1', 'b'), - (63, 'client', 'players/animations/diagonalHorizontal', '1', 'b'), - (64, 'client', 'ui/uiTarget/hideOnDialog', '0', 'b'), - (65, 'client', 'ui/uiTarget/enabled', '1', 'b'), - (66, 'client', 'ui/lifeBar/x', '5', 'i'), - (67, 'client', 'ui/lifeBar/y', '12', 'i'), - (68, 'client', 'ui/lifeBar/height', '5', 'i'), - (69, 'client', 'ui/lifeBar/width', '50', 'i'), - (70, 'client', 'ui/lifeBar/fixedPosition', '0', 'b'), - (71, 'server', 'rooms/world/tryClosestPath', '1', 'b'), - (72, 'server', 'actions/pvp/battleTimeOff', '20000', 'i'), - (73, 'server', 'actions/pvp/timerType', 'bt', 's'), - (74, 'server', 'enemies/initialStats/atk', '50', 'i'), - (75, 'server', 'enemies/initialStats/def', '50', 'i'), - (76, 'server', 'enemies/initialStats/dodge', '50', 'i'), - (77, 'server', 'enemies/initialStats/hp', '50', 'i'), - (78, 'server', 'enemies/initialStats/mp', '50', 'i'), - (79, 'server', 'enemies/initialStats/speed', '50', 'i'), - (80, 'server', 'enemies/initialStats/stamina', '50', 'i'), - (81, 'client', 'ui/pointer/show', '1', 'b'), - (82, 'server', 'enemies/default/skillKey', 'attackShort', 't'), - (83, 'client', 'players/size/topOffset', '20', 'i'), - (84, 'client', 'players/size/leftOffset', '0', 'i'), - (85, 'server', 'rooms/world/onlyWalkable', '1', 'b'), - (86, 'client', 'ui/screen/responsive', '1', 'b'), - (87, 'client', 'ui/uiTarget/responsiveY', '0', 'i'), - (88, 'client', 'ui/uiTarget/responsiveX', '0', 'i'), - (89, 'client', 'ui/inventory/enabled', '1', 'b'), - (90, 'client', 'ui/inventory/x', '380', 'i'), - (91, 'client', 'ui/inventory/y', '450', 'i'), - (92, 'client', 'ui/inventory/responsiveY', '0', 'i'), - (93, 'client', 'ui/inventory/responsiveX', '100', 'i'), - (94, 'client', 'ui/equipment/enabled', '1', 'b'), - (95, 'client', 'ui/equipment/x', '430', 'i'), - (96, 'client', 'ui/equipment/y', '90', 'i'), - (97, 'client', 'ui/equipment/responsiveY', '0', 'i'), - (98, 'client', 'ui/equipment/responsiveX', '100', 'i'), - (99, 'client', 'ui/lifeBar/responsiveY', '24', 'i'), - (100, 'client', 'ui/lifeBar/responsiveX', '1', 'i'), - (101, 'client', 'ui/sceneLabel/responsiveY', '0', 'i'), - (102, 'client', 'ui/sceneLabel/responsiveX', '50', 'i'), - (103, 'client', 'ui/playerStats/responsiveY', '0', 'i'), - (104, 'client', 'ui/playerStats/responsiveX', '100', 'i'), - (105, 'client', 'ui/playerBox/responsiveY', '0', 'i'), - (106, 'client', 'ui/playerBox/responsiveX', '0', 'i'), - (107, 'client', 'ui/controls/responsiveY', '100', 'i'), - (108, 'client', 'ui/controls/responsiveX', '0', 'i'), - (109, 'client', 'ui/chat/responsiveY', '100', 'i'), - (110, 'client', 'ui/chat/responsiveX', '100', 'i'), - (111, 'client', 'ui/chat/enabled', '1', 'b'), - (112, 'client', 'ui/npcDialog/x', '120', 'i'), - (113, 'client', 'ui/npcDialog/y', '100', 'i'), - (114, 'client', 'ui/npcDialog/responsiveX', '10', 'i'), - (115, 'client', 'ui/npcDialog/responsiveY', '10', 'i'), - (116, 'client', 'ui/maximum/x', '1280', 'i'), - (117, 'client', 'ui/maximum/y', '720', 'i'), - (118, 'client', 'ui/chat/defaultOpen', '0', 'b'), - (119, 'client', 'ui/chat/notificationBalloon', '1', 'b'), - (120, 'client', 'ui/chat/damageMessages', '1', 'b'), - (121, 'server', 'players/actions/initialClassPathId', '1', 'i'), - (122, 'server', 'enemies/initialStats/aim', '50', 'i'), - (123, 'client', 'actions/skills/affectedProperty', 'hp', 't'), - (124, 'client', 'ui/controls/opacityEffect', '1', 'b'), - (125, 'client', 'ui/skills/y', '390', 'i'), - (126, 'client', 'ui/skills/x', '230', 'i'), - (127, 'client', 'ui/skills/responsiveY', '100', 'i'), - (128, 'client', 'ui/skills/responsiveX', '0', 'i'), - (129, 'client', 'ui/skills/enabled', '1', 'b'), - (130, 'client', 'skills/animations/default_atk', '{"key":"default_atk","animationData":{"enabled":true,"type":"spritesheet","img":"default_atk","frameWidth":64,"frameHeight":64,"start":0,"end":4,"repeat":0}}', 'j'), - (131, 'client', 'skills/animations/default_bullet', '{"key":"default_bullet","animationData":{"enabled":true,"type":"spritesheet","img":"default_bullet","frameWidth":64,"frameHeight":64,"start":0,"end":2,"repeat":-1,"rate":1}}', 'j'), - (132, 'client', 'skills/animations/default_cast', '{"key": "default_cast","animationData":{"enabled":false,"type":"spritesheet","img":"default_cast","frameWidth":64,"frameHeight":64,"start":0,"end":3,"repeat":0}}', 'j'), - (133, 'client', 'skills/animations/default_death', '{"key":"default_death","animationData":{"enabled":true,"type":"spritesheet","img":"default_death","frameWidth":64,"frameHeight":64,"start":0,"end":1,"repeat":0,"rate":1}}', 'j'), - (134, 'client', 'skills/animations/default_hit', '{"key":"default_hit","animationData":{"enabled":true,"type":"spritesheet","img":"default_hit","frameWidth":64,"frameHeight":64,"start":0,"end":3,"repeat":0,"depthByPlayer":"above"}}', 'j'), - (135, 'client', 'ui/controls/defaultActionKey', '', 't'), - (136, 'client', 'players/animations/collideWorldBounds', '1', 'b'), - (137, 'server', 'rooms/world/bulletsStopOnPlayer', '1', 'b'), - (138, 'client', 'players/animations/fallbackImage', 'player-base', 't'), - (139, 'client', 'players/multiplePlayers/enabled', '1', 'b'), - (140, 'server', 'players/gameOver/timeOut', '10000', 'i'), - (141, 'client', 'ui/controls/tabTarget', '1', 'b'), - (142, 'client', 'ui/controls/disableContextMenu', '1', 'b'), - (143, 'client', 'ui/controls/primaryMove', '0', 'b'), - (144, 'client', 'ui/instructions/enabled', '1', 'b'), - (145, 'client', 'ui/instructions/responsiveX', '100', 'i'), - (146, 'client', 'ui/instructions/responsiveY', '100', 'i'), - (147, 'client', 'ui/instructions/x', '380', 'i'), - (148, 'client', 'ui/instructions/y', '940', 'i'), - (149, 'client', 'ui/players/showNames', '1', 'b'), - (157, 'client', 'ui/lifeBar/top', '5', 'i'), - (158, 'client', 'actions/damage/enabled', '1', 'b'), - (159, 'client', 'actions/damage/showAll', '0', 'b'), - (160, 'client', 'actions/damage/font', 'Verdana, Geneva, sans-serif', 't'), - (161, 'client', 'actions/damage/color', '#ff0000', 't'), - (162, 'client', 'actions/damage/duration', '600', 'i'), - (163, 'client', 'actions/damage/top', '50', 'i'), - (164, 'client', 'actions/damage/fontSize', '14', 'i'), - (165, 'client', 'actions/damage/stroke', '#000000', 't'), - (166, 'client', 'actions/damage/strokeThickness', '4', 'i'), - (167, 'client', 'actions/damage/shadowColor', 'rgba(0,0,0,0.7)', 't'), - (168, 'client', 'ui/lifeBar/fillStyle', '0xff0000', 't'), - (169, 'client', 'ui/lifeBar/lineStyle', '0xffffff', 't'), - (170, 'client', 'ui/lifeBar/showAllPlayers', '0', 'b'), - (171, 'client', 'ui/lifeBar/showEnemies', '1', 'b'), - (172, 'client', 'players/animations/defaultFrames/left/start', '3', 'i'), - (173, 'client', 'players/animations/defaultFrames/left/end', '5', 'i'), - (174, 'client', 'players/animations/defaultFrames/right/start', '6', 'i'), - (175, 'client', 'players/animations/defaultFrames/right/end', '8', 'i'), - (176, 'client', 'players/animations/defaultFrames/up/start', '9', 'i'), - (177, 'client', 'players/animations/defaultFrames/up/end', '11', 'i'), - (178, 'client', 'players/animations/defaultFrames/down/start', '0', 'i'), - (179, 'client', 'players/animations/defaultFrames/down/end', '2', 'i'), - (180, 'client', 'ui/minimap/enabled', '1', 'b'), - (181, 'client', 'ui/minimap/mapWidthDivisor', '1', 'i'), - (182, 'client', 'ui/minimap/mapHeightDivisor', '1', 'i'), - (183, 'client', 'ui/minimap/fixedWidth', '450', 'i'), - (184, 'client', 'ui/minimap/fixedHeight', '450', 'i'), - (186, 'client', 'ui/minimap/camX', '140', 'i'), - (187, 'client', 'ui/minimap/camY', '10', 'i'), - (188, 'client', 'ui/minimap/camBackgroundColor', 'rgba(0,0,0,0.6)', 't'), - (189, 'client', 'ui/minimap/camZoom', '0.35', 'i'), - (191, 'client', 'ui/minimap/addCircle', '1', 'b'), - (192, 'client', 'ui/minimap/circleX', '220', 'i'), - (193, 'client', 'ui/minimap/circleY', '88', 'i'), - (194, 'client', 'ui/minimap/circleRadio', '80.35', 'i'), - (195, 'client', 'ui/minimap/circleColor', 'rgb(0,0,0)', 't'), - (196, 'client', 'ui/minimap/circleAlpha', '1', 'i'), - (197, 'client', 'ui/minimap/circleStrokeLineWidth', '6', 'i'), - (198, 'client', 'ui/minimap/circleStrokeColor', '0', 'i'), - (199, 'client', 'ui/minimap/circleStrokeAlpha', '0.6', 'i'), - (200, 'client', 'ui/minimap/circleFillColor', '1', 'i'), - (201, 'client', 'ui/minimap/circleFillAlpha', '0', 'i'), - (202, 'client', 'ui/pointer/topOffSet', '16', 'i'), - (203, 'client', 'ui/minimap/responsiveX', '34', 'i'), - (204, 'client', 'ui/minimap/responsiveY', '2.4', 'i'), - (205, 'client', 'ui/minimap/x', '180', 'i'), - (206, 'client', 'ui/minimap/y', '10', 'i'), - (207, 'client', 'ui/settings/responsiveX', '100', 'i'), - (208, 'client', 'ui/settings/responsiveY', '100', 'i'), - (209, 'client', 'ui/settings/x', '940', 'i'), - (210, 'client', 'ui/settings/y', '280', 'i'), - (211, 'client', 'ui/settings/enabled', '1', 'b'), - (212, 'client', 'ui/lifeBar/showOnClick', '1', 'b'), - (213, 'client', 'rooms/selection/allowOnRegistration', '1', 'b'), - (214, 'client', 'rooms/selection/allowOnLogin', '1', 'b'), - (215, 'client', 'rooms/selection/registrationAvailableRooms', '*', 't'), - (216, 'client', 'rooms/selection/loginLastLocation', '1', 'b'), - (218, 'client', 'rooms/selection/loginAvailableRooms', '*', 't'), - (219, 'client', 'rooms/selection/loginLastLocationLabel', 'Last Location', 't'), - (220, 'client', 'players/tapMovement/enabled', '1', 'b'), - (221, 'client', 'ui/chat/overheadChat/enabled', '1', 'b'), - (222, 'client', 'chat/messages/characterLimit', '100', 'i'), - (223, 'client', 'chat/messages/characterLimitOverhead', '50', 'i'), - (224, 'client', 'ui/chat/overheadText/fontFamily', 'Verdana, Geneva, sans-serif', 't'), - (225, 'client', 'ui/chat/overheadText/fontSize', '12px', 't'), - (226, 'client', 'ui/chat/overheadText/fill', '#ffffff', 't'), - (227, 'client', 'ui/chat/overheadText/align', 'center', 't'), - (228, 'client', 'ui/chat/overheadText/stroke', 'rgba(0,0,0,0.7)', 't'), - (229, 'client', 'ui/chat/overheadText/strokeThickness', '20', 'i'), - (230, 'client', 'ui/chat/overheadText/shadowX', '5', 'i'), - (231, 'client', 'ui/chat/overheadText/shadowY', '5', 'i'), - (232, 'client', 'ui/chat/overheadText/shadowColor', 'rgba(0,0,0,0.7)', 't'), - (233, 'client', 'ui/chat/overheadText/shadowBlur', '5', 'i'), - (234, 'client', 'ui/chat/overheadText/depth', '200000', 'i'), - (235, 'client', 'ui/chat/overheadText/height', '15', 'i'), - (236, 'client', 'ui/chat/overheadText/textLength', '4', 'i'), - (237, 'client', 'ui/players/nameText/fontFamily', 'Verdana, Geneva, sans-serif', 't'), - (238, 'client', 'ui/players/nameText/fontSize', '12px', 't'), - (239, 'client', 'ui/players/nameText/fill', '#ffffff', 't'), - (240, 'client', 'ui/players/nameText/align', 'center', 't'), - (241, 'client', 'ui/players/nameText/stroke', '#000000', 't'), - (242, 'client', 'ui/players/nameText/strokeThickness', '4', 'i'), - (243, 'client', 'ui/players/nameText/shadowX', '5', 'i'), - (244, 'client', 'ui/players/nameText/shadowY', '5', 'i'), - (245, 'client', 'ui/players/nameText/shadowColor', 'rgba(0,0,0,0.7)', 't'), - (246, 'client', 'ui/players/nameText/shadowBlur', '5', 'i'), - (247, 'client', 'ui/players/nameText/depth', '200000', 'i'), - (248, 'client', 'ui/players/nameText/height', '-90', 'i'), - (249, 'client', 'ui/players/nameText/textLength', '4', 'i'), - (250, 'client', 'ui/chat/overheadChat/isTyping', '1', 'b'), - (251, 'client', 'ui/chat/overheadText/timeOut', '5000', 'i'), - (252, 'client', 'ui/chat/overheadChat/closeChatBoxAfterSend', '1', 'b'), - (253, 'client', 'players/playedTime/show', '2', 'i'), - (254, 'client', 'players/playedTime/label', 'Played Time:
', 't'), - (255, 'client', 'objects/npc/invalidOptionMessage', 'I do not understand.', 't'), - (256, 'client', 'ui/minimap/roundMap', '1', 'b'), - (257, 'client', 'general/engine/clientInterpolation', '1', 'b'), - (258, 'client', 'general/engine/interpolationSpeed', '0.4', 'i'), - (259, 'client', 'general/engine/experimentalClientPrediction', '0', 'b'), - (262, 'client', 'players/physicalBody/width', '25', 'i'), - (263, 'client', 'players/physicalBody/height', '25', 'i'), - (264, 'server', 'objects/actions/closeInteractionOnOutOfReach', '1', 'b'), - (265, 'client', 'trade/players/awaitTimeOut', '1', 'b'), - (266, 'client', 'trade/players/timeOut', '8000', 'i'), - (267, 'client', 'ui/default/responsiveX', '10', 'i'), - (268, 'client', 'ui/default/responsiveY', '10', 'i'), - (269, 'client', 'ui/default/x', '120', 'i'), - (270, 'client', 'ui/default/y', '100', 'i'), - (275, 'client', 'ui/trade/responsiveX', '5', 'i'), - (276, 'client', 'ui/trade/responsiveY', '5', 'i'), - (277, 'client', 'ui/trade/x', '5', 'i'), - (278, 'client', 'ui/trade/y', '5', 'i'), - (279, 'server', 'enemies/default/affectedProperty', 'stats/hp', 't'), - (280, 'client', 'ui/chat/effectMessages', '1', 'b'), - (281, 'client', 'ui/chat/dodgeMessages', '1', 'b'), - (282, 'client', 'ui/chat/totalValidTypes', '2', 'i'); + (1, 'server', 'rooms/validation/valid', 'room_game,chat_global', 1), + (2, 'server', 'players/initialState/room_id', '4', 2), + (3, 'server', 'players/initialState/x', '400', 2), + (4, 'server', 'players/initialState/y', '345', 2), + (5, 'server', 'players/initialState/dir', 'down', 1), + (13, 'server', 'rooms/validation/enabled', '1', 3), + (18, 'client', 'general/controls/allowSimultaneousKeys', '1', 3), + (19, 'server', 'rooms/world/timeStep', '0.04', 2), + (20, 'server', 'chat/messages/broadcast_join', '1', 3), + (21, 'server', 'chat/messages/broadcast_leave', '1', 3), + (22, 'server', 'chat/messages/global_enabled', '1', 3), + (23, 'server', 'chat/messages/global_allowed_roles', '1,9000', 1), + (24, 'server', 'players/physicsBody/speed', '180', 2), + (25, 'client', 'players/animations/fadeDuration', '1000', 2), + (26, 'client', 'ui/playerBox/x', '50', 2), + (27, 'client', 'ui/playerStats/enabled', '1', 3), + (28, 'client', 'ui/controls/enabled', '1', 3), + (29, 'client', 'map/tileData/width', '16', 2), + (30, 'client', 'map/tileData/height', '16', 2), + (31, 'client', 'map/tileData/margin', '1', 2), + (32, 'client', 'map/tileData/spacing', '2', 2), + (33, 'client', 'players/size/width', '52', 2), + (34, 'client', 'players/size/height', '71', 2), + (35, 'client', 'general/animations/frameRate', '10', 2), + (36, 'client', 'map/layersDepth/belowPlayer', '0', 2), + (37, 'client', 'map/layersDepth/changePoints', '0', 2), + (38, 'client', 'ui/sceneLabel/enabled', '1', 3), + (39, 'client', 'general/controls/action_button_hold', '0', 3), + (40, 'client', 'ui/chat/x', '440', 2), + (41, 'client', 'ui/chat/y', '940', 2), + (42, 'server', 'players/actions/interactionDistance', '40', 2), + (43, 'server', 'objects/actions/interactionsDistance', '140', 2), + (44, 'client', 'ui/playerBox/enabled', '1', 3), + (45, 'client', 'ui/playerBox/y', '30', 2), + (46, 'client', 'ui/lifeBar/enabled', '1', 3), + (47, 'client', 'ui/uiTarget/x', '10', 2), + (48, 'client', 'ui/uiTarget/y', '85', 2), + (49, 'client', 'ui/sceneLabel/x', '250', 2), + (50, 'client', 'ui/sceneLabel/y', '20', 2), + (51, 'client', 'ui/controls/x', '120', 2), + (52, 'client', 'ui/controls/y', '390', 2), + (53, 'client', 'ui/playerStats/x', '430', 2), + (54, 'client', 'ui/playerStats/y', '20', 2), + (55, 'client', 'ui/loading/font', 'Verdana, Geneva, sans-serif', 1), + (56, 'client', 'ui/loading/fontSize', '20px', 1), + (57, 'client', 'ui/loading/assetsSize', '18px', 1), + (58, 'client', 'ui/loading/loadingColor', '#ffffff', 1), + (59, 'client', 'ui/loading/percentColor', '#666666', 1), + (60, 'client', 'ui/loading/assetsColor', '#ffffff', 1), + (61, 'client', 'ui/loading/showAssets', '1', 3), + (62, 'client', 'players/animations/basedOnPress', '1', 3), + (63, 'client', 'players/animations/diagonalHorizontal', '1', 3), + (64, 'client', 'ui/uiTarget/hideOnDialog', '0', 3), + (65, 'client', 'ui/uiTarget/enabled', '1', 3), + (66, 'client', 'ui/lifeBar/x', '5', 2), + (67, 'client', 'ui/lifeBar/y', '12', 2), + (68, 'client', 'ui/lifeBar/height', '5', 2), + (69, 'client', 'ui/lifeBar/width', '50', 2), + (70, 'client', 'ui/lifeBar/fixedPosition', '0', 3), + (71, 'server', 'rooms/world/tryClosestPath', '1', 3), + (72, 'server', 'actions/pvp/battleTimeOff', '20000', 2), + (73, 'server', 'actions/pvp/timerType', 'bt', 1), + (74, 'server', 'enemies/initialStats/atk', '50', 2), + (75, 'server', 'enemies/initialStats/def', '50', 2), + (76, 'server', 'enemies/initialStats/dodge', '50', 2), + (77, 'server', 'enemies/initialStats/hp', '50', 2), + (78, 'server', 'enemies/initialStats/mp', '50', 2), + (79, 'server', 'enemies/initialStats/speed', '50', 2), + (80, 'server', 'enemies/initialStats/stamina', '50', 2), + (81, 'client', 'ui/pointer/show', '1', 3), + (82, 'server', 'enemies/default/skillKey', 'attackShort', 1), + (83, 'client', 'players/size/topOffset', '20', 2), + (84, 'client', 'players/size/leftOffset', '0', 2), + (85, 'server', 'rooms/world/onlyWalkable', '1', 3), + (86, 'client', 'ui/screen/responsive', '1', 3), + (87, 'client', 'ui/uiTarget/responsiveY', '0', 2), + (88, 'client', 'ui/uiTarget/responsiveX', '0', 2), + (89, 'client', 'ui/inventory/enabled', '1', 3), + (90, 'client', 'ui/inventory/x', '380', 2), + (91, 'client', 'ui/inventory/y', '450', 2), + (92, 'client', 'ui/inventory/responsiveY', '0', 2), + (93, 'client', 'ui/inventory/responsiveX', '100', 2), + (94, 'client', 'ui/equipment/enabled', '1', 3), + (95, 'client', 'ui/equipment/x', '430', 2), + (96, 'client', 'ui/equipment/y', '90', 2), + (97, 'client', 'ui/equipment/responsiveY', '0', 2), + (98, 'client', 'ui/equipment/responsiveX', '100', 2), + (99, 'client', 'ui/lifeBar/responsiveY', '24', 2), + (100, 'client', 'ui/lifeBar/responsiveX', '1', 2), + (101, 'client', 'ui/sceneLabel/responsiveY', '0', 2), + (102, 'client', 'ui/sceneLabel/responsiveX', '50', 2), + (103, 'client', 'ui/playerStats/responsiveY', '0', 2), + (104, 'client', 'ui/playerStats/responsiveX', '100', 2), + (105, 'client', 'ui/playerBox/responsiveY', '0', 2), + (106, 'client', 'ui/playerBox/responsiveX', '0', 2), + (107, 'client', 'ui/controls/responsiveY', '100', 2), + (108, 'client', 'ui/controls/responsiveX', '0', 2), + (109, 'client', 'ui/chat/responsiveY', '100', 2), + (110, 'client', 'ui/chat/responsiveX', '100', 2), + (111, 'client', 'ui/chat/enabled', '1', 3), + (112, 'client', 'ui/npcDialog/x', '120', 2), + (113, 'client', 'ui/npcDialog/y', '100', 2), + (114, 'client', 'ui/npcDialog/responsiveX', '10', 2), + (115, 'client', 'ui/npcDialog/responsiveY', '10', 2), + (116, 'client', 'ui/maximum/x', '1280', 2), + (117, 'client', 'ui/maximum/y', '720', 2), + (118, 'client', 'ui/chat/defaultOpen', '0', 3), + (119, 'client', 'ui/chat/notificationBalloon', '1', 3), + (120, 'client', 'ui/chat/damageMessages', '1', 3), + (121, 'server', 'players/actions/initialClassPathId', '1', 2), + (122, 'server', 'enemies/initialStats/aim', '50', 2), + (123, 'client', 'actions/skills/affectedProperty', 'hp', 1), + (124, 'client', 'ui/controls/opacityEffect', '1', 3), + (125, 'client', 'ui/skills/y', '390', 2), + (126, 'client', 'ui/skills/x', '230', 2), + (127, 'client', 'ui/skills/responsiveY', '100', 2), + (128, 'client', 'ui/skills/responsiveX', '0', 2), + (129, 'client', 'ui/skills/enabled', '1', 3), + (130, 'client', 'skills/animations/default_atk', '{"key":"default_atk","animationData":{"enabled":true,"type":"spritesheet","img":"default_atk","frameWidth":64,"frameHeight":64,"start":0,"end":4,"repeat":0}}', 4), + (131, 'client', 'skills/animations/default_bullet', '{"key":"default_bullet","animationData":{"enabled":true,"type":"spritesheet","img":"default_bullet","frameWidth":64,"frameHeight":64,"start":0,"end":2,"repeat":-1,"rate":1}}', 4), + (132, 'client', 'skills/animations/default_cast', '{"key": "default_cast","animationData":{"enabled":false,"type":"spritesheet","img":"default_cast","frameWidth":64,"frameHeight":64,"start":0,"end":3,"repeat":0}}', 4), + (133, 'client', 'skills/animations/default_death', '{"key":"default_death","animationData":{"enabled":true,"type":"spritesheet","img":"default_death","frameWidth":64,"frameHeight":64,"start":0,"end":1,"repeat":0,"rate":1}}', 4), + (134, 'client', 'skills/animations/default_hit', '{"key":"default_hit","animationData":{"enabled":true,"type":"spritesheet","img":"default_hit","frameWidth":64,"frameHeight":64,"start":0,"end":3,"repeat":0,"depthByPlayer":"above"}}', 4), + (135, 'client', 'ui/controls/defaultActionKey', '', 1), + (136, 'client', 'players/animations/collideWorldBounds', '1', 3), + (137, 'server', 'rooms/world/bulletsStopOnPlayer', '1', 3), + (138, 'client', 'players/animations/fallbackImage', 'player-base', 1), + (139, 'client', 'players/multiplePlayers/enabled', '1', 3), + (140, 'server', 'players/gameOver/timeOut', '10000', 2), + (141, 'client', 'ui/controls/tabTarget', '1', 3), + (142, 'client', 'ui/controls/disableContextMenu', '1', 3), + (143, 'client', 'ui/controls/primaryMove', '0', 3), + (144, 'client', 'ui/instructions/enabled', '1', 3), + (145, 'client', 'ui/instructions/responsiveX', '100', 2), + (146, 'client', 'ui/instructions/responsiveY', '100', 2), + (147, 'client', 'ui/instructions/x', '380', 2), + (148, 'client', 'ui/instructions/y', '940', 2), + (149, 'client', 'ui/players/showNames', '1', 3), + (157, 'client', 'ui/lifeBar/top', '5', 2), + (158, 'client', 'actions/damage/enabled', '1', 3), + (159, 'client', 'actions/damage/showAll', '0', 3), + (160, 'client', 'actions/damage/font', 'Verdana, Geneva, sans-serif', 1), + (161, 'client', 'actions/damage/color', '#ff0000', 1), + (162, 'client', 'actions/damage/duration', '600', 2), + (163, 'client', 'actions/damage/top', '50', 2), + (164, 'client', 'actions/damage/fontSize', '14', 2), + (165, 'client', 'actions/damage/stroke', '#000000', 1), + (166, 'client', 'actions/damage/strokeThickness', '4', 2), + (167, 'client', 'actions/damage/shadowColor', 'rgba(0,0,0,0.7)', 1), + (168, 'client', 'ui/lifeBar/fillStyle', '0xff0000', 1), + (169, 'client', 'ui/lifeBar/lineStyle', '0xffffff', 1), + (170, 'client', 'ui/lifeBar/showAllPlayers', '0', 3), + (171, 'client', 'ui/lifeBar/showEnemies', '1', 3), + (172, 'client', 'players/animations/defaultFrames/left/start', '3', 2), + (173, 'client', 'players/animations/defaultFrames/left/end', '5', 2), + (174, 'client', 'players/animations/defaultFrames/right/start', '6', 2), + (175, 'client', 'players/animations/defaultFrames/right/end', '8', 2), + (176, 'client', 'players/animations/defaultFrames/up/start', '9', 2), + (177, 'client', 'players/animations/defaultFrames/up/end', '11', 2), + (178, 'client', 'players/animations/defaultFrames/down/start', '0', 2), + (179, 'client', 'players/animations/defaultFrames/down/end', '2', 2), + (180, 'client', 'ui/minimap/enabled', '1', 3), + (181, 'client', 'ui/minimap/mapWidthDivisor', '1', 2), + (182, 'client', 'ui/minimap/mapHeightDivisor', '1', 2), + (183, 'client', 'ui/minimap/fixedWidth', '450', 2), + (184, 'client', 'ui/minimap/fixedHeight', '450', 2), + (186, 'client', 'ui/minimap/camX', '140', 2), + (187, 'client', 'ui/minimap/camY', '10', 2), + (188, 'client', 'ui/minimap/camBackgroundColor', 'rgba(0,0,0,0.6)', 1), + (189, 'client', 'ui/minimap/camZoom', '0.35', 2), + (191, 'client', 'ui/minimap/addCircle', '1', 3), + (192, 'client', 'ui/minimap/circleX', '220', 2), + (193, 'client', 'ui/minimap/circleY', '88', 2), + (194, 'client', 'ui/minimap/circleRadio', '80.35', 2), + (195, 'client', 'ui/minimap/circleColor', 'rgb(0,0,0)', 1), + (196, 'client', 'ui/minimap/circleAlpha', '1', 2), + (197, 'client', 'ui/minimap/circleStrokeLineWidth', '6', 2), + (198, 'client', 'ui/minimap/circleStrokeColor', '0', 2), + (199, 'client', 'ui/minimap/circleStrokeAlpha', '0.6', 2), + (200, 'client', 'ui/minimap/circleFillColor', '1', 2), + (201, 'client', 'ui/minimap/circleFillAlpha', '0', 2), + (202, 'client', 'ui/pointer/topOffSet', '16', 2), + (203, 'client', 'ui/minimap/responsiveX', '34', 2), + (204, 'client', 'ui/minimap/responsiveY', '2.4', 2), + (205, 'client', 'ui/minimap/x', '180', 2), + (206, 'client', 'ui/minimap/y', '10', 2), + (207, 'client', 'ui/settings/responsiveX', '100', 2), + (208, 'client', 'ui/settings/responsiveY', '100', 2), + (209, 'client', 'ui/settings/x', '940', 2), + (210, 'client', 'ui/settings/y', '280', 2), + (211, 'client', 'ui/settings/enabled', '1', 3), + (212, 'client', 'ui/lifeBar/showOnClick', '1', 3), + (213, 'client', 'rooms/selection/allowOnRegistration', '1', 3), + (214, 'client', 'rooms/selection/allowOnLogin', '1', 3), + (215, 'client', 'rooms/selection/registrationAvailableRooms', '*', 1), + (216, 'client', 'rooms/selection/loginLastLocation', '1', 3), + (218, 'client', 'rooms/selection/loginAvailableRooms', '*', 1), + (219, 'client', 'rooms/selection/loginLastLocationLabel', 'Last Location', 1), + (220, 'client', 'players/tapMovement/enabled', '1', 3), + (221, 'client', 'ui/chat/overheadChat/enabled', '1', 3), + (222, 'client', 'chat/messages/characterLimit', '100', 2), + (223, 'client', 'chat/messages/characterLimitOverhead', '50', 2), + (224, 'client', 'ui/chat/overheadText/fontFamily', 'Verdana, Geneva, sans-serif', 1), + (225, 'client', 'ui/chat/overheadText/fontSize', '12px', 1), + (226, 'client', 'ui/chat/overheadText/fill', '#ffffff', 1), + (227, 'client', 'ui/chat/overheadText/align', 'center', 1), + (228, 'client', 'ui/chat/overheadText/stroke', 'rgba(0,0,0,0.7)', 1), + (229, 'client', 'ui/chat/overheadText/strokeThickness', '20', 2), + (230, 'client', 'ui/chat/overheadText/shadowX', '5', 2), + (231, 'client', 'ui/chat/overheadText/shadowY', '5', 2), + (232, 'client', 'ui/chat/overheadText/shadowColor', 'rgba(0,0,0,0.7)', 1), + (233, 'client', 'ui/chat/overheadText/shadowBlur', '5', 2), + (234, 'client', 'ui/chat/overheadText/depth', '200000', 2), + (235, 'client', 'ui/chat/overheadText/height', '15', 2), + (236, 'client', 'ui/chat/overheadText/textLength', '4', 2), + (237, 'client', 'ui/players/nameText/fontFamily', 'Verdana, Geneva, sans-serif', 1), + (238, 'client', 'ui/players/nameText/fontSize', '12px', 1), + (239, 'client', 'ui/players/nameText/fill', '#ffffff', 1), + (240, 'client', 'ui/players/nameText/align', 'center', 1), + (241, 'client', 'ui/players/nameText/stroke', '#000000', 1), + (242, 'client', 'ui/players/nameText/strokeThickness', '4', 2), + (243, 'client', 'ui/players/nameText/shadowX', '5', 2), + (244, 'client', 'ui/players/nameText/shadowY', '5', 2), + (245, 'client', 'ui/players/nameText/shadowColor', 'rgba(0,0,0,0.7)', 1), + (246, 'client', 'ui/players/nameText/shadowBlur', '5', 2), + (247, 'client', 'ui/players/nameText/depth', '200000', 2), + (248, 'client', 'ui/players/nameText/height', '-90', 2), + (249, 'client', 'ui/players/nameText/textLength', '4', 2), + (250, 'client', 'ui/chat/overheadChat/isTyping', '1', 3), + (251, 'client', 'ui/chat/overheadText/timeOut', '5000', 2), + (252, 'client', 'ui/chat/overheadChat/closeChatBoxAfterSend', '1', 3), + (253, 'client', 'players/playedTime/show', '2', 2), + (254, 'client', 'players/playedTime/label', 'Played time %playedTimeInHs hs', 1), + (255, 'client', 'objects/npc/invalidOptionMessage', 'I do not understand.', 1), + (256, 'client', 'ui/minimap/roundMap', '1', 3), + (257, 'client', 'general/engine/clientInterpolation', '1', 3), + (258, 'client', 'general/engine/interpolationSpeed', '0.4', 2), + (259, 'client', 'general/engine/experimentalClientPrediction', '0', 3), + (262, 'client', 'players/physicalBody/width', '25', 2), + (263, 'client', 'players/physicalBody/height', '25', 2), + (264, 'server', 'objects/actions/closeInteractionOnOutOfReach', '1', 3), + (265, 'client', 'trade/players/awaitTimeOut', '1', 3), + (266, 'client', 'trade/players/timeOut', '8000', 2), + (267, 'client', 'ui/default/responsiveX', '10', 2), + (268, 'client', 'ui/default/responsiveY', '10', 2), + (269, 'client', 'ui/default/x', '120', 2), + (270, 'client', 'ui/default/y', '100', 2), + (275, 'client', 'ui/trade/responsiveX', '5', 2), + (276, 'client', 'ui/trade/responsiveY', '5', 2), + (277, 'client', 'ui/trade/x', '5', 2), + (278, 'client', 'ui/trade/y', '5', 2), + (279, 'server', 'enemies/default/affectedProperty', 'stats/hp', 1), + (280, 'client', 'ui/chat/effectMessages', '1', 3), + (281, 'client', 'ui/chat/dodgeMessages', '1', 3), + (282, 'client', 'ui/chat/totalValidTypes', '2', 2), + (283, 'client', 'ui/teams/enabled', '1', 3), + (284, 'client', 'ui/teams/responsiveX', '100', 2), + (285, 'client', 'ui/teams/responsiveY', '0', 2), + (286, 'client', 'ui/teams/x', '430', 2), + (287, 'client', 'ui/teams/y', '100', 2), + (288, 'client', 'ui/teams/sharedProperties', '{"hp":{"path":"stats/hp","pathMax":"statsBase/hp","label":"HP"},"mp":{"path":"stats/mp","pathMax":"statsBase/mp","label":"MP"}}', 4), + (289, 'client', 'ui/options/acceptOrDecline', '{"1":{"label":"Accept","value":1},"2":{"label":"Decline","value":2}}', 4), + (290, 'client', 'team/labels/requestFromTitle', 'Team request from:', 1), + (291, 'client', 'team/labels/leaderNameTitle', 'Team leader: %leaderName', 1), + (293, 'client', 'ui/controls/allowPrimaryTouch', '1', 3), + (294, 'client', 'ui/clan/enabled', '1', 3), + (295, 'client', 'ui/clan/responsiveX', '100', 2), + (296, 'client', 'ui/clan/responsiveY', '0', 2), + (297, 'client', 'ui/clan/x', '430', 2), + (298, 'client', 'ui/clan/y', '100', 2), + (299, 'client', 'ui/clan/sharedProperties', '{"hp":{"path":"stats/hp","pathMax":"statsBase/hp","label":"HP"},"mp":{"path":"stats/mp","pathMax":"statsBase/mp","label":"MP"}}', 4), + (300, 'client', 'clan/labels/requestFromTitle', 'Clan request from:', 1), + (302, 'client', 'clan/labels/leaderNameTitle', 'Clan leader: %leaderName', 1), + (303, 'client', 'clan/labels/propertyMaxValue', '/ %propertyMaxValue', 1), + (304, 'server', 'rewards/actions/interactionsDistance', '40', 2), + (305, 'server', 'rewards/actions/disappearTime', '1800000', 2), + (306, 'client', 'rewards/titles/rewardMessage', 'You obtained %dropQuantity %itemLabel', 1), + (307, 'client', 'clan/general/openInvites', '0', 3), + (311, 'client', 'login/termsAndConditions/link', 'Accept our Terms and Conditions (click here).', 1), + (312, 'client', 'login/termsAndConditions/heading', 'Terms and conditions', 1), + (313, 'client', 'login/termsAndConditions/body', 'This is our test terms and conditions content.', 1), + (314, 'client', 'login/termsAndConditions/checkboxLabel', 'Accept terms and conditions', 1); + +-- Dumping structure for table config_types +CREATE TABLE IF NOT EXISTS `config_types` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `label` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- Dumping data for table config_types: ~5 rows (approximately) +INSERT INTO `config_types` (`id`, `label`) VALUES + (1, 'string'), + (2, 'float'), + (3, 'boolean'), + (4, 'json'), + (5, 'comma_separated'); -- Dumping structure for table features CREATE TABLE IF NOT EXISTS `features` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, - `is_enabled` int NOT NULL, + `is_enabled` tinyint unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; --- Dumping data for table features: ~9 rows (approximately) +-- Dumping data for table features: ~13 rows (approximately) INSERT INTO `features` (`id`, `code`, `title`, `is_enabled`) VALUES (1, 'chat', 'Chat', 1), (2, 'objects', 'Objects', 1), @@ -429,20 +538,22 @@ INSERT INTO `features` (`id`, `code`, `title`, `is_enabled`) VALUES (8, 'audio', 'Audio', 1), (9, 'rooms', 'Rooms', 1), (10, 'admin', 'Admin', 1), - (11, 'prediction', 'Prediction', 0); + (11, 'prediction', 'Prediction', 0), + (12, 'teams', 'Teams', 1), + (13, 'rewards', 'Rewards', 1); -- Dumping structure for table items_group CREATE TABLE IF NOT EXISTS `items_group` ( - `id` int NOT NULL AUTO_INCREMENT, + `id` int unsigned NOT NULL AUTO_INCREMENT, `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `description` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, - `files_name` text COLLATE utf8_unicode_ci, + `files_name` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, `sort` int DEFAULT NULL, `items_limit` int NOT NULL DEFAULT '0', `limit_per_item` int NOT NULL DEFAULT '0', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='The group table is to save the groups settings.'; +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='The group table is to save the groups settings.'; -- Dumping data for table items_group: ~6 rows (approximately) INSERT INTO `items_group` (`id`, `key`, `label`, `description`, `files_name`, `sort`, `items_limit`, `limit_per_item`) VALUES @@ -455,36 +566,36 @@ INSERT INTO `items_group` (`id`, `key`, `label`, `description`, `files_name`, `s -- Dumping structure for table items_inventory CREATE TABLE IF NOT EXISTS `items_inventory` ( - `id` int NOT NULL AUTO_INCREMENT, - `owner_id` int NOT NULL, - `item_id` int NOT NULL, + `id` int unsigned NOT NULL AUTO_INCREMENT, + `owner_id` int unsigned NOT NULL, + `item_id` int unsigned NOT NULL, `qty` int NOT NULL DEFAULT '0', `remaining_uses` int DEFAULT NULL, `is_active` int DEFAULT NULL COMMENT 'For example equipped or not equipped items.', PRIMARY KEY (`id`), KEY `FK_items_inventory_items_item` (`item_id`), CONSTRAINT `FK_items_inventory_items_item` FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='Inventory table is to save the items for each owner.'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Inventory table is to save the items for each owner.'; -- Dumping structure for table items_item CREATE TABLE IF NOT EXISTS `items_item` ( - `id` int NOT NULL AUTO_INCREMENT, + `id` int unsigned NOT NULL AUTO_INCREMENT, `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `type` int NOT NULL DEFAULT '0', - `group_id` int DEFAULT NULL, + `group_id` int unsigned DEFAULT NULL, `label` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, `qty_limit` int NOT NULL DEFAULT '0' COMMENT 'Default 0 to unlimited qty.', `uses_limit` int NOT NULL DEFAULT '1' COMMENT 'Default 1 use per item (0 = unlimited).', `useTimeOut` int DEFAULT NULL, `execTimeOut` int DEFAULT NULL, - `customData` text COLLATE utf8_unicode_ci, + `customData` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`), UNIQUE KEY `key` (`key`), KEY `group_id` (`group_id`), CONSTRAINT `FK_items_item_items_group` FOREIGN KEY (`group_id`) REFERENCES `items_group` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='List of all available items in the system.'; +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='List of all available items in the system.'; -- Dumping data for table items_item: ~6 rows (approximately) INSERT INTO `items_item` (`id`, `key`, `type`, `group_id`, `label`, `description`, `qty_limit`, `uses_limit`, `useTimeOut`, `execTimeOut`, `customData`) VALUES @@ -497,8 +608,8 @@ INSERT INTO `items_item` (`id`, `key`, `type`, `group_id`, `label`, `description -- Dumping structure for table items_item_modifiers CREATE TABLE IF NOT EXISTS `items_item_modifiers` ( - `id` int NOT NULL AUTO_INCREMENT, - `item_id` int NOT NULL, + `id` int unsigned NOT NULL AUTO_INCREMENT, + `item_id` int unsigned NOT NULL, `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `operation` int NOT NULL, @@ -507,7 +618,7 @@ CREATE TABLE IF NOT EXISTS `items_item_modifiers` ( PRIMARY KEY (`id`), KEY `item_id` (`item_id`), CONSTRAINT `FK_items_item_modifiers_items_item` FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='Modifiers is the way we will affect the item owner.'; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Modifiers is the way we will affect the item owner.'; -- Dumping data for table items_item_modifiers: ~4 rows (approximately) INSERT INTO `items_item_modifiers` (`id`, `item_id`, `key`, `property_key`, `operation`, `value`, `maxProperty`) VALUES @@ -534,7 +645,7 @@ CREATE TABLE IF NOT EXISTS `objects` ( KEY `room_id` (`room_id`), KEY `object_class_key` (`object_class_key`), CONSTRAINT `FK_objects_rooms` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table objects: ~9 rows (approximately) INSERT INTO `objects` (`id`, `room_id`, `layer_name`, `tile_index`, `object_class_key`, `client_key`, `title`, `private_params`, `client_params`, `enabled`) VALUES @@ -559,7 +670,7 @@ CREATE TABLE IF NOT EXISTS `objects_animations` ( KEY `id` (`id`) USING BTREE, KEY `object_id` (`object_id`) USING BTREE, CONSTRAINT `FK_objects_animations_objects` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table objects_animations: ~4 rows (approximately) INSERT INTO `objects_animations` (`id`, `object_id`, `animationKey`, `animationData`) VALUES @@ -580,9 +691,9 @@ CREATE TABLE IF NOT EXISTS `objects_assets` ( PRIMARY KEY (`object_asset_id`), KEY `object_id` (`object_id`), CONSTRAINT `FK_objects_assets_objects` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; --- Dumping data for table objects_assets: ~8 rows (approximately) +-- Dumping data for table objects_assets: ~9 rows (approximately) INSERT INTO `objects_assets` (`object_asset_id`, `object_id`, `asset_type`, `asset_key`, `file_1`, `file_2`, `extra_params`) VALUES (1, 1, 'spritesheet', 'door_house_1', 'door-a-x2', NULL, '{"frameWidth":32,"frameHeight":58}'), (2, 4, 'spritesheet', 'door_house_2', 'door-a-x2', NULL, '{"frameWidth":32,"frameHeight":58}'), @@ -598,18 +709,18 @@ INSERT INTO `objects_assets` (`object_asset_id`, `object_id`, `asset_type`, `ass CREATE TABLE IF NOT EXISTS `objects_items_inventory` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `owner_id` int unsigned NOT NULL, - `item_id` int NOT NULL, + `item_id` int unsigned NOT NULL, `qty` int NOT NULL DEFAULT '0', `remaining_uses` int DEFAULT NULL, `is_active` int DEFAULT NULL COMMENT 'For example equipped or not equipped items.', PRIMARY KEY (`id`) USING BTREE, KEY `FK_items_inventory_items_item` (`item_id`) USING BTREE, KEY `FK_objects_items_inventory_objects` (`owner_id`), - CONSTRAINT `FK_objects_items_inventory_objects` FOREIGN KEY (`owner_id`) REFERENCES `objects` (`id`), - CONSTRAINT `objects_items_inventory_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci ROW_FORMAT=COMPACT COMMENT='Inventory table is to save the items for each owner.'; + CONSTRAINT `FK_objects_items_inventory_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`) ON UPDATE CASCADE, + CONSTRAINT `FK_objects_items_inventory_objects` FOREIGN KEY (`owner_id`) REFERENCES `objects` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=COMPACT COMMENT='Inventory table is to save the items for each owner.'; --- Dumping data for table objects_items_inventory: ~3 rows (approximately) +-- Dumping data for table objects_items_inventory: ~4 rows (approximately) INSERT INTO `objects_items_inventory` (`id`, `owner_id`, `item_id`, `qty`, `remaining_uses`, `is_active`) VALUES (2, 10, 4, -1, -1, 0), (3, 10, 5, -1, -1, 0), @@ -620,8 +731,8 @@ INSERT INTO `objects_items_inventory` (`id`, `owner_id`, `item_id`, `qty`, `rema CREATE TABLE IF NOT EXISTS `objects_items_requirements` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `object_id` int unsigned NOT NULL, - `item_key` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '', - `required_item_key` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '', + `item_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '', + `required_item_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '', `required_quantity` int unsigned NOT NULL DEFAULT '0', `auto_remove_requirement` int unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), @@ -631,7 +742,7 @@ CREATE TABLE IF NOT EXISTS `objects_items_requirements` ( CONSTRAINT `FK_objects_items_requirements_items_item` FOREIGN KEY (`item_key`) REFERENCES `items_item` (`key`), CONSTRAINT `FK_objects_items_requirements_items_item_2` FOREIGN KEY (`required_item_key`) REFERENCES `items_item` (`key`), CONSTRAINT `FK_objects_items_requirements_objects` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table objects_items_requirements: ~4 rows (approximately) INSERT INTO `objects_items_requirements` (`id`, `object_id`, `item_key`, `required_item_key`, `required_quantity`, `auto_remove_requirement`) VALUES @@ -655,15 +766,33 @@ CREATE TABLE IF NOT EXISTS `objects_items_rewards` ( CONSTRAINT `FK_objects_items_rewards_items_item` FOREIGN KEY (`item_key`) REFERENCES `items_item` (`key`), CONSTRAINT `FK_objects_items_rewards_items_item_2` FOREIGN KEY (`reward_item_key`) REFERENCES `items_item` (`key`), CONSTRAINT `objects_items_rewards_ibfk_1` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci ROW_FORMAT=COMPACT; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=COMPACT; --- Dumping data for table objects_items_rewards: ~3 rows (approximately) +-- Dumping data for table objects_items_rewards: ~4 rows (approximately) INSERT INTO `objects_items_rewards` (`id`, `object_id`, `item_key`, `reward_item_key`, `reward_quantity`, `reward_item_is_required`) VALUES (1, 10, 'axe', 'coins', 2, 0), (2, 10, 'spear', 'coins', 1, 0), (3, 10, 'heal_potion_20', 'coins', 1, 0), (5, 10, 'magic_potion_20', 'coins', 1, 0); +-- Dumping structure for table objects_items_rewards_animations +CREATE TABLE IF NOT EXISTS `objects_items_rewards_animations` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `reward_id` int unsigned NOT NULL, + `asset_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `asset_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `file` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `extra_params` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`) USING BTREE, + KEY `FK_objects_items_rewards_animations_rewards` (`reward_id`) USING BTREE, + CONSTRAINT `FK_objects_items_rewards_animations_rewards` FOREIGN KEY (`reward_id`) REFERENCES `rewards` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- Dumping data for table objects_items_rewards_animations: ~2 rows (approximately) +INSERT INTO `objects_items_rewards_animations` (`id`, `reward_id`, `asset_type`, `asset_key`, `file`, `extra_params`) VALUES + (1, 2, 'spritesheet', 'branch-sprite', 'branch-sprite', '{"start":0,"end":2,"repeat":-1,"frameWidth":32, "frameHeight":32,"depthByPlayer":"above"}'), + (2, 1, 'spritesheet', 'branch-sprite', 'branch-sprite', '{"start":0,"end":2,"repeat":-1,"frameWidth":32, "frameHeight":32,"depthByPlayer":"above"}'); + -- Dumping structure for table objects_skills CREATE TABLE IF NOT EXISTS `objects_skills` ( `id` int unsigned NOT NULL AUTO_INCREMENT, @@ -677,7 +806,7 @@ CREATE TABLE IF NOT EXISTS `objects_skills` ( CONSTRAINT `FK_objects_skills_objects` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`) ON UPDATE CASCADE, CONSTRAINT `FK_objects_skills_skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE, CONSTRAINT `FK_objects_skills_target_options` FOREIGN KEY (`target`) REFERENCES `target_options` (`target_key`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table objects_skills: ~1 rows (approximately) INSERT INTO `objects_skills` (`id`, `object_id`, `skill_id`, `target`) VALUES @@ -696,7 +825,7 @@ CREATE TABLE IF NOT EXISTS `objects_stats` ( KEY `object_id` (`object_id`) USING BTREE, CONSTRAINT `FK_object_current_stats_objects` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, CONSTRAINT `FK_objects_current_stats_objects_stats` FOREIGN KEY (`stat_id`) REFERENCES `stats` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table objects_stats: ~20 rows (approximately) INSERT INTO `objects_stats` (`id`, `object_id`, `stat_id`, `base_value`, `value`) VALUES @@ -724,11 +853,11 @@ INSERT INTO `objects_stats` (`id`, `object_id`, `stat_id`, `base_value`, `value` -- Dumping structure for table operation_types CREATE TABLE IF NOT EXISTS `operation_types` ( `id` int unsigned NOT NULL AUTO_INCREMENT, - `label` varchar(50) COLLATE utf8_unicode_ci NOT NULL, + `label` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `key` int unsigned NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `key` (`key`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table operation_types: ~9 rows (approximately) INSERT INTO `operation_types` (`id`, `label`, `key`) VALUES @@ -752,7 +881,7 @@ CREATE TABLE IF NOT EXISTS `players` ( UNIQUE KEY `name` (`name`), KEY `FK_players_users` (`user_id`), CONSTRAINT `FK_players_users` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table players: ~2 rows (approximately) INSERT INTO `players` (`id`, `user_id`, `name`, `created_at`) VALUES @@ -771,11 +900,11 @@ CREATE TABLE IF NOT EXISTS `players_state` ( KEY `FK_player_state_player_stats` (`player_id`), CONSTRAINT `FK_player_state_player_stats` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON UPDATE CASCADE, CONSTRAINT `FK_player_state_rooms` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table players_state: ~2 rows (approximately) INSERT INTO `players_state` (`id`, `player_id`, `room_id`, `x`, `y`, `dir`) VALUES - (1, 1, 5, 978, 681, 'down'); + (1, 1, 5, 947, 717, 'left'); -- Dumping structure for table players_stats CREATE TABLE IF NOT EXISTS `players_stats` ( @@ -790,13 +919,13 @@ CREATE TABLE IF NOT EXISTS `players_stats` ( KEY `user_id` (`player_id`) USING BTREE, CONSTRAINT `FK_player_current_stats_players` FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, CONSTRAINT `FK_players_current_stats_players_stats` FOREIGN KEY (`stat_id`) REFERENCES `stats` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=161 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=211 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table players_stats: ~20 rows (approximately) INSERT INTO `players_stats` (`id`, `player_id`, `stat_id`, `base_value`, `value`) VALUES - (1, 1, 1, 280, 158), - (2, 1, 2, 280, 125), - (3, 1, 3, 280, 371), + (1, 1, 1, 280, 115), + (2, 1, 2, 280, 219), + (3, 1, 3, 280, 385), (4, 1, 4, 280, 280), (5, 1, 5, 100, 100), (6, 1, 6, 100, 100), @@ -815,13 +944,54 @@ CREATE TABLE IF NOT EXISTS `respawn` ( PRIMARY KEY (`id`), KEY `respawn_object_id` (`object_id`), CONSTRAINT `FK_respawn_objects` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table respawn: ~2 rows (approximately) INSERT INTO `respawn` (`id`, `object_id`, `respawn_time`, `instances_limit`, `layer`) VALUES (3, 6, 20000, 2, 'respawn-area-monsters-lvl-1-2'), (4, 7, 10000, 3, 'respawn-area-monsters-lvl-1-2'); +-- Dumping structure for table rewards +CREATE TABLE IF NOT EXISTS `rewards` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `object_id` int unsigned NOT NULL, + `item_id` int unsigned DEFAULT NULL, + `modifier_id` int unsigned DEFAULT NULL, + `experience` int unsigned NOT NULL DEFAULT '0', + `drop_rate` int unsigned NOT NULL, + `drop_quantity` int unsigned NOT NULL, + `is_unique` tinyint unsigned NOT NULL DEFAULT '0', + `was_given` tinyint unsigned NOT NULL DEFAULT '0', + `has_drop_body` tinyint unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) USING BTREE, + KEY `FK_rewards_items_item` (`item_id`) USING BTREE, + KEY `FK_rewards_objects` (`object_id`) USING BTREE, + KEY `FK_rewards_rewards_modifiers` (`modifier_id`), + CONSTRAINT `FK_rewards_items_item` FOREIGN KEY (`item_id`) REFERENCES `items_item` (`id`), + CONSTRAINT `FK_rewards_objects` FOREIGN KEY (`object_id`) REFERENCES `objects` (`id`), + CONSTRAINT `FK_rewards_rewards_modifiers` FOREIGN KEY (`modifier_id`) REFERENCES `rewards_modifiers` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- Dumping data for table rewards: ~2 rows (approximately) +INSERT INTO `rewards` (`id`, `object_id`, `item_id`, `modifier_id`, `experience`, `drop_rate`, `drop_quantity`, `is_unique`, `was_given`, `has_drop_body`) VALUES + (1, 7, 2, NULL, 10, 100, 1, 0, 0, 1), + (2, 6, 2, NULL, 10, 100, 3, 0, 0, 1); + +-- Dumping structure for table rewards_modifiers +CREATE TABLE IF NOT EXISTS `rewards_modifiers` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `property_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `operation` int unsigned NOT NULL, + `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `minValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `maxValue` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `minProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `maxProperty` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `modifier_id` (`key`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + -- Dumping structure for table rooms CREATE TABLE IF NOT EXISTS `rooms` ( `id` int unsigned NOT NULL AUTO_INCREMENT, @@ -833,9 +1003,9 @@ CREATE TABLE IF NOT EXISTS `rooms` ( `customData` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, PRIMARY KEY (`id`), UNIQUE KEY `key` (`name`) -) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; --- Dumping data for table rooms: ~5 rows (approximately) +-- Dumping data for table rooms: ~6 rows (approximately) INSERT INTO `rooms` (`id`, `name`, `title`, `map_filename`, `scene_images`, `room_class_key`, `customData`) VALUES (2, 'ReldensHouse_1', 'House - 1', 'reldens-house-1', 'reldens-house-1', NULL, NULL), (3, 'ReldensHouse_2', 'House - 2', 'reldens-house-2', 'reldens-house-2', NULL, NULL), @@ -856,7 +1026,7 @@ CREATE TABLE IF NOT EXISTS `rooms_change_points` ( KEY `FK_rooms_change_points_rooms_2` (`next_room_id`), CONSTRAINT `FK_rooms_change_points_rooms` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) ON UPDATE CASCADE, CONSTRAINT `FK_rooms_change_points_rooms_2` FOREIGN KEY (`next_room_id`) REFERENCES `rooms` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table rooms_change_points: ~17 rows (approximately) INSERT INTO `rooms_change_points` (`id`, `room_id`, `tile_index`, `next_room_id`) VALUES @@ -892,7 +1062,7 @@ CREATE TABLE IF NOT EXISTS `rooms_return_points` ( KEY `FK_scenes_return_points_rooms_2` (`from_room_id`) USING BTREE, CONSTRAINT `FK_rooms_return_points_rooms_from_room_id` FOREIGN KEY (`from_room_id`) REFERENCES `rooms` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, CONSTRAINT `FK_rooms_return_points_rooms_room_id` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table rooms_return_points: ~10 rows (approximately) INSERT INTO `rooms_return_points` (`id`, `room_id`, `direction`, `x`, `y`, `is_default`, `from_room_id`) VALUES @@ -918,9 +1088,9 @@ CREATE TABLE IF NOT EXISTS `skills_class_level_up_animations` ( KEY `FK_skills_class_level_up_skills_levels` (`level_id`) USING BTREE, CONSTRAINT `FK_skills_class_level_up_skills_class_path` FOREIGN KEY (`class_path_id`) REFERENCES `skills_class_path` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, CONSTRAINT `FK_skills_class_level_up_skills_levels` FOREIGN KEY (`level_id`) REFERENCES `skills_levels` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; --- Dumping data for table skills_class_level_up_animations: ~0 rows (approximately) +-- Dumping data for table skills_class_level_up_animations: ~1 rows (approximately) INSERT INTO `skills_class_level_up_animations` (`id`, `class_path_id`, `level_id`, `animationData`) VALUES (1, NULL, NULL, '{"enabled":true,"type":"spritesheet","img":"heal_cast","frameWidth":64,"frameHeight":70,"start":0,"end":3,"repeat":-1,"destroyTime":2000,"depthByPlayer":"above"}'); @@ -934,7 +1104,7 @@ CREATE TABLE IF NOT EXISTS `skills_class_path` ( UNIQUE KEY `key` (`key`), KEY `levels_set_id` (`levels_set_id`), CONSTRAINT `FK_skills_class_path_skills_levels_set` FOREIGN KEY (`levels_set_id`) REFERENCES `skills_levels_set` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table skills_class_path: ~5 rows (approximately) INSERT INTO `skills_class_path` (`id`, `key`, `label`, `levels_set_id`) VALUES @@ -956,7 +1126,7 @@ CREATE TABLE IF NOT EXISTS `skills_class_path_level_labels` ( KEY `level_key` (`level_id`) USING BTREE, CONSTRAINT `FK__skills_class_path` FOREIGN KEY (`class_path_id`) REFERENCES `skills_class_path` (`id`) ON UPDATE CASCADE, CONSTRAINT `FK_skills_class_path_level_labels_skills_levels` FOREIGN KEY (`level_id`) REFERENCES `skills_levels` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table skills_class_path_level_labels: ~5 rows (approximately) INSERT INTO `skills_class_path_level_labels` (`id`, `class_path_id`, `level_id`, `label`) VALUES @@ -980,7 +1150,7 @@ CREATE TABLE IF NOT EXISTS `skills_class_path_level_skills` ( CONSTRAINT `FK_skills_class_path_level_skills_skills_levels` FOREIGN KEY (`level_id`) REFERENCES `skills_levels` (`key`) ON UPDATE CASCADE, CONSTRAINT `FK_skills_class_path_level_skills_skills_levels_id` FOREIGN KEY (`level_id`) REFERENCES `skills_levels` (`id`) ON UPDATE CASCADE, CONSTRAINT `FK_skills_class_path_level_skills_skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table skills_class_path_level_skills: ~15 rows (approximately) INSERT INTO `skills_class_path_level_skills` (`id`, `class_path_id`, `level_id`, `skill_id`) VALUES @@ -1008,7 +1178,7 @@ CREATE TABLE IF NOT EXISTS `skills_groups` ( `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `sort` int unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping structure for table skills_levels CREATE TABLE IF NOT EXISTS `skills_levels` ( @@ -1021,7 +1191,7 @@ CREATE TABLE IF NOT EXISTS `skills_levels` ( UNIQUE KEY `key_level_set_id` (`key`,`level_set_id`), KEY `level_set_id` (`level_set_id`), CONSTRAINT `FK_skills_levels_skills_levels_set` FOREIGN KEY (`level_set_id`) REFERENCES `skills_levels_set` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table skills_levels: ~20 rows (approximately) INSERT INTO `skills_levels` (`id`, `key`, `label`, `required_experience`, `level_set_id`) VALUES @@ -1064,7 +1234,7 @@ CREATE TABLE IF NOT EXISTS `skills_levels_modifiers` ( KEY `FK_skills_levels_modifiers_operation_types` (`operation`), CONSTRAINT `FK_skills_levels_modifiers_operation_types` FOREIGN KEY (`operation`) REFERENCES `operation_types` (`key`), CONSTRAINT `FK_skills_levels_modifiers_skills_levels` FOREIGN KEY (`level_id`) REFERENCES `skills_levels` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=121 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='Modifiers table.'; +) ENGINE=InnoDB AUTO_INCREMENT=121 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Modifiers table.'; -- Dumping data for table skills_levels_modifiers: ~120 rows (approximately) INSERT INTO `skills_levels_modifiers` (`id`, `level_id`, `key`, `property_key`, `operation`, `value`, `minValue`, `maxValue`, `minProperty`, `maxProperty`) VALUES @@ -1208,7 +1378,7 @@ CREATE TABLE IF NOT EXISTS `skills_levels_set` ( `autoFillRanges` int unsigned NOT NULL DEFAULT '0', `autoFillExperienceMultiplier` int unsigned DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table skills_levels_set: ~5 rows (approximately) INSERT INTO `skills_levels_set` (`id`, `autoFillRanges`, `autoFillExperienceMultiplier`) VALUES @@ -1228,11 +1398,11 @@ CREATE TABLE IF NOT EXISTS `skills_owners_class_path` ( PRIMARY KEY (`id`), KEY `level_set_id` (`class_path_id`) USING BTREE, CONSTRAINT `FK_skills_owners_class_path_skills_class_path` FOREIGN KEY (`class_path_id`) REFERENCES `skills_class_path` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table skills_owners_class_path: ~2 rows (approximately) INSERT INTO `skills_owners_class_path` (`id`, `class_path_id`, `owner_id`, `currentLevel`, `currentExp`) VALUES - (1, 1, 1, 10, 8480); + (1, 1, 1, 10, 9080); -- Dumping structure for table skills_skill CREATE TABLE IF NOT EXISTS `skills_skill` ( @@ -1256,7 +1426,7 @@ CREATE TABLE IF NOT EXISTS `skills_skill` ( `customData` text CHARACTER SET utf8 COLLATE utf8_unicode_ci COMMENT 'Any custom data, recommended JSON format.', PRIMARY KEY (`id`) USING BTREE, UNIQUE KEY `key` (`key`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table skills_skill: ~4 rows (approximately) INSERT INTO `skills_skill` (`id`, `key`, `type`, `autoValidation`, `skillDelay`, `castTime`, `usesLimit`, `range`, `rangeAutomaticValidation`, `rangePropertyX`, `rangePropertyY`, `rangeTargetPropertyX`, `rangeTargetPropertyY`, `allowSelfTarget`, `criticalChance`, `criticalMultiplier`, `criticalFixedValue`, `customData`) VALUES @@ -1278,7 +1448,7 @@ CREATE TABLE IF NOT EXISTS `skills_skill_animations` ( KEY `key` (`key`) USING BTREE, KEY `skill_id` (`skill_id`) USING BTREE, CONSTRAINT `FK_skills_skill_animations_skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table skills_skill_animations: ~4 rows (approximately) INSERT INTO `skills_skill_animations` (`id`, `skill_id`, `key`, `classKey`, `animationData`) VALUES @@ -1306,13 +1476,13 @@ CREATE TABLE IF NOT EXISTS `skills_skill_attack` ( PRIMARY KEY (`id`), KEY `skill_id` (`skill_id`), CONSTRAINT `FK__skills_skill_attack` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table skills_skill_attack: ~3 rows (approximately) INSERT INTO `skills_skill_attack` (`id`, `skill_id`, `affectedProperty`, `allowEffectBelowZero`, `hitDamage`, `applyDirectDamage`, `attackProperties`, `defenseProperties`, `aimProperties`, `dodgeProperties`, `dodgeFullEnabled`, `dodgeOverAimSuccess`, `damageAffected`, `criticalAffected`) VALUES - (1, 1, 'stats/hp', 0, 3, 0, 'stats/atk,stats/stamina,stats/speed', 'stats/def,stats/stamina,stats/speed', 'stats/aim', 'stats/dodge', 1, 1, 0, 0), - (2, 2, 'stats/hp', 0, 5, 0, 'stats/atk,stats/stamina,stats/speed', 'stats/def,stats/stamina,stats/speed', 'stats/aim', 'stats/dodge', 1, 1, 0, 0), - (3, 3, 'stats/hp', 0, 7, 0, 'stats/atk,stats/stamina,stats/speed', 'stats/def,stats/stamina,stats/speed', 'stats/aim', 'stats/dodge', 1, 1, 0, 0); + (1, 1, 'stats/hp', 0, 3, 0, 'stats/atk,stats/stamina,stats/speed', 'stats/def,stats/stamina,stats/speed', 'stats/aim', 'stats/dodge', 0, 1, 0, 0), + (2, 2, 'stats/hp', 0, 5, 0, 'stats/atk,stats/stamina,stats/speed', 'stats/def,stats/stamina,stats/speed', 'stats/aim', 'stats/dodge', 0, 1, 0, 0), + (3, 3, 'stats/hp', 0, 7, 0, 'stats/atk,stats/stamina,stats/speed', 'stats/def,stats/stamina,stats/speed', 'stats/aim', 'stats/dodge', 0, 1, 0, 0); -- Dumping structure for table skills_skill_group_relation CREATE TABLE IF NOT EXISTS `skills_skill_group_relation` ( @@ -1324,7 +1494,7 @@ CREATE TABLE IF NOT EXISTS `skills_skill_group_relation` ( KEY `skill_id` (`skill_id`), CONSTRAINT `FK__skills_groups` FOREIGN KEY (`group_id`) REFERENCES `skills_groups` (`id`) ON UPDATE CASCADE, CONSTRAINT `FK__skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping structure for table skills_skill_owner_conditions CREATE TABLE IF NOT EXISTS `skills_skill_owner_conditions` ( @@ -1339,7 +1509,7 @@ CREATE TABLE IF NOT EXISTS `skills_skill_owner_conditions` ( CONSTRAINT `FK_skills_skill_owner_conditions_skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf32 COLLATE=utf32_unicode_ci; --- Dumping data for table skills_skill_owner_conditions: ~0 rows (approximately) +-- Dumping data for table skills_skill_owner_conditions: ~1 rows (approximately) INSERT INTO `skills_skill_owner_conditions` (`id`, `skill_id`, `key`, `property_key`, `conditional`, `value`) VALUES (1, 3, 'available_mp', 'stats/mp', 'ge', '5'); @@ -1360,7 +1530,7 @@ CREATE TABLE IF NOT EXISTS `skills_skill_owner_effects` ( KEY `FK_skills_skill_owner_effects_operation_types` (`operation`), CONSTRAINT `FK_skills_skill_owner_effects_operation_types` FOREIGN KEY (`operation`) REFERENCES `operation_types` (`key`) ON UPDATE CASCADE, CONSTRAINT `FK_skills_skill_owner_effects_skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='Modifiers table.'; +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Modifiers table.'; -- Dumping data for table skills_skill_owner_effects: ~2 rows (approximately) INSERT INTO `skills_skill_owner_effects` (`id`, `skill_id`, `key`, `property_key`, `operation`, `value`, `minValue`, `maxValue`, `minProperty`, `maxProperty`) VALUES @@ -1391,7 +1561,7 @@ CREATE TABLE IF NOT EXISTS `skills_skill_physical_data` ( PRIMARY KEY (`id`), KEY `attack_skill_id` (`skill_id`) USING BTREE, CONSTRAINT `FK_skills_skill_physical_data_skills_skill` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table skills_skill_physical_data: ~2 rows (approximately) INSERT INTO `skills_skill_physical_data` (`id`, `skill_id`, `magnitude`, `objectWidth`, `objectHeight`, `validateTargetOnHit`) VALUES @@ -1415,9 +1585,9 @@ CREATE TABLE IF NOT EXISTS `skills_skill_target_effects` ( KEY `FK_skills_skill_target_effects_operation_types` (`operation`), CONSTRAINT `FK_skills_skill_effect_modifiers` FOREIGN KEY (`skill_id`) REFERENCES `skills_skill` (`id`) ON UPDATE CASCADE, CONSTRAINT `FK_skills_skill_target_effects_operation_types` FOREIGN KEY (`operation`) REFERENCES `operation_types` (`key`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci COMMENT='Modifiers table.'; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Modifiers table.'; --- Dumping data for table skills_skill_target_effects: ~0 rows (approximately) +-- Dumping data for table skills_skill_target_effects: ~1 rows (approximately) INSERT INTO `skills_skill_target_effects` (`id`, `skill_id`, `key`, `property_key`, `operation`, `value`, `minValue`, `maxValue`, `minProperty`, `maxProperty`) VALUES (1, 4, 'heal', 'stats/hp', 1, '10', '0', '0', NULL, 'statsBase/hp'); @@ -1444,7 +1614,7 @@ CREATE TABLE IF NOT EXISTS `stats` ( `customData` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, PRIMARY KEY (`id`) USING BTREE, UNIQUE KEY `key` (`key`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table stats: ~10 rows (approximately) INSERT INTO `stats` (`id`, `key`, `label`, `description`, `base_value`, `customData`) VALUES @@ -1463,10 +1633,10 @@ INSERT INTO `stats` (`id`, `key`, `label`, `description`, `base_value`, `customD CREATE TABLE IF NOT EXISTS `target_options` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `target_key` tinyint unsigned NOT NULL, - `target_label` varchar(50) COLLATE utf8_unicode_ci NOT NULL, + `target_label` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `target_key` (`target_key`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table target_options: ~2 rows (approximately) INSERT INTO `target_options` (`id`, `target_key`, `target_label`) VALUES @@ -1487,11 +1657,11 @@ CREATE TABLE IF NOT EXISTS `users` ( PRIMARY KEY (`id`), UNIQUE KEY `email` (`email`), UNIQUE KEY `username` (`username`) -) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- Dumping data for table users: ~2 rows (approximately) INSERT INTO `users` (`id`, `email`, `username`, `password`, `role_id`, `status`, `created_at`, `updated_at`, `played_time`) VALUES - (1, 'dap@dap.com', 'dap', '$2b$10$RDnURyFoXo7.zcFKVhNcuezJsXXYNslhPBNPzi.crbikFhG8Pnude', 1, '1', '2022-03-17 18:57:44', '2023-01-05 12:03:19', 649132); + (1, 'dap@dap.com', 'dap', '$2b$10$RDnURyFoXo7.zcFKVhNcuezJsXXYNslhPBNPzi.crbikFhG8Pnude', 1, '1', '2022-03-17 18:57:44', '2023-05-07 13:28:48', 794406); /*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */; /*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; diff --git a/package-lock.json b/package-lock.json index b97e347f6..7922ecf53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,20 @@ { "name": "reldens", - "version": "4.0.0-beta.25", + "version": "4.0.0-beta.26", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "reldens", - "version": "4.0.0-beta.25", + "version": "4.0.0-beta.26", "license": "MIT", "dependencies": { "@adminjs/design-system": "2.1.2", "@adminjs/express": "4.1.0", "@adminjs/upload": "2.0.2", - "@colyseus/monitor": "^0.14.22", - "@colyseus/schema": "^2.0.4", - "@colyseus/ws-transport": "^0.14.21", + "@colyseus/monitor": "0.14.22", + "@colyseus/schema": "2.0.4", + "@colyseus/ws-transport": "0.14.21", "@parcel/bundler-default": "2.8.0", "@parcel/compressor-raw": "2.8.0", "@parcel/config-default": "2.8.0", @@ -53,33 +53,33 @@ "@parcel/transformer-svg": "2.8.0", "@parcel/transformer-webmanifest": "2.8.0", "@parcel/transformer-worklet": "2.8.0", - "@reldens/items-system": "^0.16.3", - "@reldens/modifiers": "^0.17.1", - "@reldens/skills": "^0.16.6", - "@reldens/storage": "^0.12.2", - "@reldens/utils": "^0.17.8", + "@reldens/items-system": "^0.18.1", + "@reldens/modifiers": "^0.19.1", + "@reldens/skills": "^0.18.0", + "@reldens/storage": "^0.15.0", + "@reldens/utils": "^0.20.0", "adminjs": "5.7.3", - "bcrypt": "^5.1.0", + "bcrypt": "5.1.0", "colyseus": "0.14.24", "colyseus.js": "0.14.13", - "core-js": "^3.26.1", + "core-js": "3.26.1", "cors": "2.8.5", - "dotenv": "^16.0.3", - "express": "^4.18.2", - "express-basic-auth": "^1.2.1", - "express-formidable": "^1.2.0", - "express-session": "^1.17.3", + "dotenv": "16.0.3", + "express": "4.18.2", + "express-basic-auth": "1.2.1", + "express-formidable": "1.2.0", + "express-session": "1.17.3", "firebase": "9.9.3", "firebaseui": "6.0.2", - "mime-types": "^2.1.35", - "mustache": "^4.2.0", - "nodemailer": "^6.8.0", - "p2": "^0.7.1", - "pathfinding": "^0.4.18", + "mime-types": "2.1.35", + "mustache": "4.2.0", + "nodemailer": "6.8.0", + "p2": "0.7.1", + "pathfinding": "0.4.18", "phaser": "3.55.2", - "react": "^16.13.1", - "regenerator-runtime": "^0.13.11", - "tslib": "^2.4.1" + "react": "16.14.0", + "regenerator-runtime": "0.13.11", + "tslib": "2.5.0" }, "engines": { "node": ">=18.0.0", @@ -146,9 +146,9 @@ } }, "node_modules/@aws-crypto/ie11-detection": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.2.tgz", - "integrity": "sha512-5XDMQY98gMAf/WRTic5G++jfmS/VLM0rwpiOpaainKi4L0nqWMSB1SzsrEG5rjFZGYN6ZAefO+/Yta2dFM0kMw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", "optional": true, "dependencies": { "tslib": "^1.11.1" @@ -161,16 +161,16 @@ "optional": true }, "node_modules/@aws-crypto/sha256-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", - "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", "optional": true, "dependencies": { - "@aws-crypto/ie11-detection": "^2.0.0", - "@aws-crypto/sha256-js": "^2.0.0", - "@aws-crypto/supports-web-crypto": "^2.0.0", - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" @@ -183,13 +183,13 @@ "optional": true }, "node_modules/@aws-crypto/sha256-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", - "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", "optional": true, "dependencies": { - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "tslib": "^1.11.1" } }, @@ -200,9 +200,9 @@ "optional": true }, "node_modules/@aws-crypto/supports-web-crypto": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.2.tgz", - "integrity": "sha512-6mbSsLHwZ99CTOOswvCRP3C+VCWnzBf+1SnbWxzzJ9lR0mA0JnY2JEAhp8rqmTE0GPFy88rrM27ffgp62oErMQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", "optional": true, "dependencies": { "tslib": "^1.11.1" @@ -215,12 +215,12 @@ "optional": true }, "node_modules/@aws-crypto/util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.2.tgz", - "integrity": "sha512-Lgu5v/0e/BcrZ5m/IWqzPUf3UYFTy/PpeED+uc9SWUR1iZQL8XXbGQg10UfllwwBryO3hFF5dizK+78aoXC1eA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", "optional": true, "dependencies": { - "@aws-sdk/types": "^3.110.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" } @@ -232,510 +232,502 @@ "optional": true }, "node_modules/@aws-sdk/abort-controller": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.226.0.tgz", - "integrity": "sha512-cJVzr1xxPBd08voknXvR0RLgtZKGKt6WyDpH/BaPCu3rfSqWCDZKzwqe940eqosjmKrxC6pUZNKASIqHOQ8xxQ==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.303.0.tgz", + "integrity": "sha512-LzNzpeyTppcmV/6SAQI3T/huOkMrUnFveplgVNwJxw+rVqmqmGV6z6vpg+oRICRDcjXWYiSiaClxxSVvOy0sDQ==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.238.0.tgz", - "integrity": "sha512-vjIVFzlkcUX9YT7pqg9NrvOTuqqsdYSesFgMLTCSWXBE5rPjWEK5ljIVOZtGn8MJQgwqS+Lqc8oB1vIfE+EaSA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.303.0.tgz", + "integrity": "sha512-rybplTjq6aj7DttT+v8ycajT8BIFXqdo66lkQjO1YykEIyVTnY4L9McTyNFOZsvNmG1LMSqb95/eYP463Lp7fg==", "optional": true, "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.238.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.238.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.303.0", + "@aws-sdk/config-resolver": "3.303.0", + "@aws-sdk/credential-provider-node": "3.303.0", + "@aws-sdk/fetch-http-handler": "3.303.0", + "@aws-sdk/hash-node": "3.303.0", + "@aws-sdk/invalid-dependency": "3.303.0", + "@aws-sdk/middleware-content-length": "3.303.0", + "@aws-sdk/middleware-endpoint": "3.303.0", + "@aws-sdk/middleware-host-header": "3.303.0", + "@aws-sdk/middleware-logger": "3.303.0", + "@aws-sdk/middleware-recursion-detection": "3.303.0", + "@aws-sdk/middleware-retry": "3.303.0", + "@aws-sdk/middleware-serde": "3.303.0", + "@aws-sdk/middleware-signing": "3.303.0", + "@aws-sdk/middleware-stack": "3.303.0", + "@aws-sdk/middleware-user-agent": "3.303.0", + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/node-http-handler": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/smithy-client": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "@aws-sdk/util-base64": "3.303.0", + "@aws-sdk/util-body-length-browser": "3.303.0", + "@aws-sdk/util-body-length-node": "3.303.0", + "@aws-sdk/util-defaults-mode-browser": "3.303.0", + "@aws-sdk/util-defaults-mode-node": "3.303.0", + "@aws-sdk/util-endpoints": "3.303.0", + "@aws-sdk/util-retry": "3.303.0", + "@aws-sdk/util-user-agent-browser": "3.303.0", + "@aws-sdk/util-user-agent-node": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.238.0.tgz", - "integrity": "sha512-KHJJWP7hBDa9KLYiU5+hOb+3AAba93PhWebXkpKyQ/Bs+e7ECCreyLCwuME6uWTV01NDuFDpwZ6zUMpyNIcP6Q==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.303.0.tgz", + "integrity": "sha512-LZ+Z6vGnEdqmxx0dqtZP97n5VX5uUKu4lJmDR3sdGolxAUqCY1FxHDZd9DzCFXR8rwoJK4VJTL+exzeVp4Ly/g==", "optional": true, "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.303.0", + "@aws-sdk/fetch-http-handler": "3.303.0", + "@aws-sdk/hash-node": "3.303.0", + "@aws-sdk/invalid-dependency": "3.303.0", + "@aws-sdk/middleware-content-length": "3.303.0", + "@aws-sdk/middleware-endpoint": "3.303.0", + "@aws-sdk/middleware-host-header": "3.303.0", + "@aws-sdk/middleware-logger": "3.303.0", + "@aws-sdk/middleware-recursion-detection": "3.303.0", + "@aws-sdk/middleware-retry": "3.303.0", + "@aws-sdk/middleware-serde": "3.303.0", + "@aws-sdk/middleware-stack": "3.303.0", + "@aws-sdk/middleware-user-agent": "3.303.0", + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/node-http-handler": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/smithy-client": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "@aws-sdk/util-base64": "3.303.0", + "@aws-sdk/util-body-length-browser": "3.303.0", + "@aws-sdk/util-body-length-node": "3.303.0", + "@aws-sdk/util-defaults-mode-browser": "3.303.0", + "@aws-sdk/util-defaults-mode-node": "3.303.0", + "@aws-sdk/util-endpoints": "3.303.0", + "@aws-sdk/util-retry": "3.303.0", + "@aws-sdk/util-user-agent-browser": "3.303.0", + "@aws-sdk/util-user-agent-node": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.238.0.tgz", - "integrity": "sha512-kazcA2Kp+cXQRtaZi5/T5YFfU9J3nzu1tXJsh0xAm+J3S9LS1ertY1bSX6KBed2xuxx2mfum8JRqli0TJad/pA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.303.0.tgz", + "integrity": "sha512-oOdDcBjxGiJ6mFWUMVr+A1hAzGRpcZ+oLAhCakpvpXCUG50PZSBFP+vOQXgHY/XNolqDg+IHq60oE9HoPzGleg==", "optional": true, "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.303.0", + "@aws-sdk/fetch-http-handler": "3.303.0", + "@aws-sdk/hash-node": "3.303.0", + "@aws-sdk/invalid-dependency": "3.303.0", + "@aws-sdk/middleware-content-length": "3.303.0", + "@aws-sdk/middleware-endpoint": "3.303.0", + "@aws-sdk/middleware-host-header": "3.303.0", + "@aws-sdk/middleware-logger": "3.303.0", + "@aws-sdk/middleware-recursion-detection": "3.303.0", + "@aws-sdk/middleware-retry": "3.303.0", + "@aws-sdk/middleware-serde": "3.303.0", + "@aws-sdk/middleware-stack": "3.303.0", + "@aws-sdk/middleware-user-agent": "3.303.0", + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/node-http-handler": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/smithy-client": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "@aws-sdk/util-base64": "3.303.0", + "@aws-sdk/util-body-length-browser": "3.303.0", + "@aws-sdk/util-body-length-node": "3.303.0", + "@aws-sdk/util-defaults-mode-browser": "3.303.0", + "@aws-sdk/util-defaults-mode-node": "3.303.0", + "@aws-sdk/util-endpoints": "3.303.0", + "@aws-sdk/util-retry": "3.303.0", + "@aws-sdk/util-user-agent-browser": "3.303.0", + "@aws-sdk/util-user-agent-node": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-sts": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.238.0.tgz", - "integrity": "sha512-jQNwHqxWUGvWCN4o8KUFYQES8r41Oobu7x1KZOMrPhPxy27FUcDjBq/h85VoD2/AZlETSCZLiCnKV3KBh5pT5w==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.303.0.tgz", + "integrity": "sha512-oda7mOfGyJZe62DZ5BVH3L84yeDM0Ja/fSpTjwV9hqFqzgtW83TCpiNegcJmvmGWDYaPmE2qpfDPqPzymB0sBg==", "optional": true, "dependencies": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.238.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-sdk-sts": "3.226.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "fast-xml-parser": "4.0.11", - "tslib": "^2.3.1" + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.303.0", + "@aws-sdk/credential-provider-node": "3.303.0", + "@aws-sdk/fetch-http-handler": "3.303.0", + "@aws-sdk/hash-node": "3.303.0", + "@aws-sdk/invalid-dependency": "3.303.0", + "@aws-sdk/middleware-content-length": "3.303.0", + "@aws-sdk/middleware-endpoint": "3.303.0", + "@aws-sdk/middleware-host-header": "3.303.0", + "@aws-sdk/middleware-logger": "3.303.0", + "@aws-sdk/middleware-recursion-detection": "3.303.0", + "@aws-sdk/middleware-retry": "3.303.0", + "@aws-sdk/middleware-sdk-sts": "3.303.0", + "@aws-sdk/middleware-serde": "3.303.0", + "@aws-sdk/middleware-signing": "3.303.0", + "@aws-sdk/middleware-stack": "3.303.0", + "@aws-sdk/middleware-user-agent": "3.303.0", + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/node-http-handler": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/smithy-client": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "@aws-sdk/util-base64": "3.303.0", + "@aws-sdk/util-body-length-browser": "3.303.0", + "@aws-sdk/util-body-length-node": "3.303.0", + "@aws-sdk/util-defaults-mode-browser": "3.303.0", + "@aws-sdk/util-defaults-mode-node": "3.303.0", + "@aws-sdk/util-endpoints": "3.303.0", + "@aws-sdk/util-retry": "3.303.0", + "@aws-sdk/util-user-agent-browser": "3.303.0", + "@aws-sdk/util-user-agent-node": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/config-resolver": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.234.0.tgz", - "integrity": "sha512-uZxy4wzllfvgCQxVc+Iqhde0NGAnfmV2hWR6ejadJaAFTuYNvQiRg9IqJy3pkyDPqXySiJ8Bom5PoJfgn55J/A==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.303.0.tgz", + "integrity": "sha512-uGZ47jcH86AwWcjZjuOL5jK5qE4izrEol8oF7KY214kjmavbKQstyUqmcwL2lr/YpDNFkCYgUxWRpduqVm8zmw==", "optional": true, "dependencies": { - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-config-provider": "3.295.0", + "@aws-sdk/util-middleware": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.238.0.tgz", - "integrity": "sha512-FGVXGtGn7t/XacBdCKgqzxJNvorjCIar+F0oRsGq8aH09L/H8uGZ7qXvYvi9I94Qv7ay0Dy/KpGcMB53zt41UA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.303.0.tgz", + "integrity": "sha512-9MYsGJCNLsm61PW/JFm4y0Cv6aluCkZmE5D/g4vYnEFOZSKyK15m1a10RKGAh391fh6Bg1kU9WOoqkGk3Nyqng==", "optional": true, "dependencies": { - "@aws-sdk/client-cognito-identity": "3.238.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-cognito-identity": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.226.0.tgz", - "integrity": "sha512-sd8uK1ojbXxaZXlthzw/VXZwCPUtU3PjObOfr3Evj7MPIM2IH8h29foOlggx939MdLQGboJf9gKvLlvKDWtJRA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.303.0.tgz", + "integrity": "sha512-rtXumfF4cGrVk9fWACeLCfdpmlzlDUkzwSR60/3enC5Antcxl3fFY5T1BzNFvz0mB0zcwm4kaAwIcljX67DNRA==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-imds": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.226.0.tgz", - "integrity": "sha512-//z/COQm2AjYFI1Lb0wKHTQSrvLFTyuKLFQGPJsKS7DPoxGOCKB7hmYerlbl01IDoCxTdyL//TyyPxbZEOQD5Q==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.303.0.tgz", + "integrity": "sha512-ruomcFkKUpJkZb87em698//A0AVpt1KN9dn8N8eVyOuvZzebVxSW4AJoVgOKd5Av4PVcZgEqRX0kOOVp0iTrWg==", "optional": true, "dependencies": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.238.0.tgz", - "integrity": "sha512-WmPNtIYyUasjV7VQxvPNq7ihmx0vFsiKAtjNjjakdrt5TPoma4nUYb9tIG9SuG+kcp4DJIgRLJAgZtXbCcVimg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.303.0.tgz", + "integrity": "sha512-4J50F6fEjQmAstSQOpJFG+rnbEqtwA7nDG6PxNm98VSTH2mYJV0YgBdvydfBKrKINAT4xYZta5Sc4WEIpSo0TA==", "optional": true, "dependencies": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.238.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/credential-provider-env": "3.303.0", + "@aws-sdk/credential-provider-imds": "3.303.0", + "@aws-sdk/credential-provider-process": "3.303.0", + "@aws-sdk/credential-provider-sso": "3.303.0", + "@aws-sdk/credential-provider-web-identity": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.238.0.tgz", - "integrity": "sha512-/RN5EyGfgdIIJdFzv+O0nSaHX1/F3anQjTIBeVg8GJ+82m+bDxMdALsG+NzkYnLilN9Uhc1lSNjLBCoPa5DSEg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.303.0.tgz", + "integrity": "sha512-OlKb7O2jDtrzkzLT/PUb5kxuGGTIyPn2alXzGT+7LdJ9/tP8KlqSVMtnH2UYPPdcc/daK16+MRNL5ylxmnRJ7Q==", "optional": true, "dependencies": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.238.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.238.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/credential-provider-env": "3.303.0", + "@aws-sdk/credential-provider-imds": "3.303.0", + "@aws-sdk/credential-provider-ini": "3.303.0", + "@aws-sdk/credential-provider-process": "3.303.0", + "@aws-sdk/credential-provider-sso": "3.303.0", + "@aws-sdk/credential-provider-web-identity": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.226.0.tgz", - "integrity": "sha512-iUDMdnrTvbvaCFhWwqyXrhvQ9+ojPqPqXhwZtY1X/Qaz+73S9gXBPJHZaZb2Ke0yKE1Ql3bJbKvmmxC/qLQMng==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.303.0.tgz", + "integrity": "sha512-1pxDYRscGlERAjFE5hSF1KQdcyOGzssuRTdLvez4I/mSIOAJLMmBAnmHGI/DME2LzDVrC9dklA6LHSC2sn3quQ==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.238.0.tgz", - "integrity": "sha512-i70V4bFlCVYey3QARJ6XxKEg/4YuoFRnePV2oK37UHOGpEn49uXKwVZqLjzJgFHln7BPlC06cWDqrHUQIMvYrQ==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.303.0.tgz", + "integrity": "sha512-/szzM1BzZGjHwV4mSiZo65cyDleJqnxM9Y4autg55mb3dFwcCiMGI6TGbdegumrNZZlCTeTA1lIhA9PdT4gDAQ==", "optional": true, "dependencies": { - "@aws-sdk/client-sso": "3.238.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/token-providers": "3.238.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/token-providers": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.226.0.tgz", - "integrity": "sha512-CCpv847rLB0SFOHz2igvUMFAzeT2fD3YnY4C8jltuJoEkn0ITn1Hlgt13nTJ5BUuvyti2mvyXZHmNzhMIMrIlw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.303.0.tgz", + "integrity": "sha512-qi5CP4ocseqdj3kMi0vgLx8XrdanLNvCAfgiEF6LjUJI88R2snZAYNUSd+Y2n04mKAalns+mUwfUN2JyL66d5g==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/credential-providers": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.238.0.tgz", - "integrity": "sha512-wqj88z9UCbqxnd9XoaKrI+7oSN/13f1AP7cZWYKrzdvb3Ae5QmXmgMKXJdsQlWtPijchpPOQNGhd1rVogycgiA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.303.0.tgz", + "integrity": "sha512-ueO8UKvYyzt2lexvIdg50TFC7EO2shRWbMWPsVi6Ul7euoQzthr/TPQts4OLZIt9XeIFd4s9dhFwYSobcRfVGw==", "optional": true, "dependencies": { - "@aws-sdk/client-cognito-identity": "3.238.0", - "@aws-sdk/client-sso": "3.238.0", - "@aws-sdk/client-sts": "3.238.0", - "@aws-sdk/credential-provider-cognito-identity": "3.238.0", - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.238.0", - "@aws-sdk/credential-provider-node": "3.238.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.238.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-cognito-identity": "3.303.0", + "@aws-sdk/client-sso": "3.303.0", + "@aws-sdk/client-sts": "3.303.0", + "@aws-sdk/credential-provider-cognito-identity": "3.303.0", + "@aws-sdk/credential-provider-env": "3.303.0", + "@aws-sdk/credential-provider-imds": "3.303.0", + "@aws-sdk/credential-provider-ini": "3.303.0", + "@aws-sdk/credential-provider-node": "3.303.0", + "@aws-sdk/credential-provider-process": "3.303.0", + "@aws-sdk/credential-provider-sso": "3.303.0", + "@aws-sdk/credential-provider-web-identity": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/fetch-http-handler": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.226.0.tgz", - "integrity": "sha512-JewZPMNEBXfi1xVnRa7pVtK/zgZD8/lQ/YnD8pq79WuMa2cwyhDtr8oqCoqsPW+WJT5ScXoMtuHxN78l8eKWgg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.303.0.tgz", + "integrity": "sha512-Bc6C86/KQOSWPa741h9QEVcApyignYV5vC5+zZjmKkcyPxrVxTmL3kTJidpVOtVfCmTIrNN/WhAVDzLBbh1ycQ==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/querystring-builder": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-base64": "3.303.0", + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/hash-node": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.226.0.tgz", - "integrity": "sha512-MdlJhJ9/Espwd0+gUXdZRsHuostB2WxEVAszWxobP0FTT9PnicqnfK7ExmW+DUAc0ywxtEbR3e0UND65rlSTVw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.303.0.tgz", + "integrity": "sha512-jSo4A/JxTabZ9jHrx7nhKIXnOmvPg/SSYnoHaFdVS5URJrNt1w+nSvW1wLGMEMOvu5+NU3bldBBSb+h0Ocwv1A==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-buffer-from": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/invalid-dependency": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.226.0.tgz", - "integrity": "sha512-QXOYFmap8g9QzRjumcRCIo2GEZkdCwd7ePQW0OABWPhKHzlJ74vvBxywjU3s39EEBEluWXtZ7Iufg6GxZM4ifw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.303.0.tgz", + "integrity": "sha512-RXNcLxOrUJaMMqk5uIYEf6X9XCMockT27bS8Dde/0ms015VOo8Wn2hHU9wEmGeFvLccC2UU4gPzvmj74w70q2Q==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/is-array-buffer": { - "version": "3.201.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.201.0.tgz", - "integrity": "sha512-UPez5qLh3dNgt0DYnPD/q0mVJY84rA17QE26hVNOW3fAji8W2wrwrxdacWOxyXvlxWsVRcKmr+lay1MDqpAMfg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.303.0.tgz", + "integrity": "sha512-IitBTr+pou7v5BrYLFH/SbIf3g1LIgMhcI3bDXBq2FjzmDftj4bW8BOmg05b9YKf2TrrggvJ4yk/jH+yYFXoJQ==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-content-length": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.226.0.tgz", - "integrity": "sha512-ksUzlHJN2JMuyavjA46a4sctvnrnITqt2tbGGWWrAuXY1mel2j+VbgnmJUiwHKUO6bTFBBeft5Vd1TSOb4JmiA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.303.0.tgz", + "integrity": "sha512-0UL5TWSL1JRpjT6gjGsZXfia5oL7vxzj+CfMCqkP6gjVF69eRcgu426Xc6TJwDcr6jIFPeamDBTLyt9ZAAr6hg==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-endpoint": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.226.0.tgz", - "integrity": "sha512-EvLFafjtUxTT0AC9p3aBQu1/fjhWdIeK58jIXaNFONfZ3F8QbEYUPuF/SqZvJM6cWfOO9qwYKkRDbCSTYhprIg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.303.0.tgz", + "integrity": "sha512-z2i8LJ6YTKbqXh9rY/KbXihvhq6P0JVI6SnkwT2hesJp0Nfldx85jsaLzj1+ioNKlQ+51u9UmBnO404DgNCAbg==", "optional": true, "dependencies": { - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-serde": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "@aws-sdk/util-middleware": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.226.0.tgz", - "integrity": "sha512-haVkWVh6BUPwKgWwkL6sDvTkcZWvJjv8AgC8jiQuSl8GLZdzHTB8Qhi3IsfFta9HAuoLjxheWBE5Z/L0UrfhLA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.303.0.tgz", + "integrity": "sha512-LUyhtjbuosrD0QAsBZJwT3yp146I7Xjehf42OP3dWbRuklMEilI0Res5K2/nknf3/ZKUj6sf7BbJoU8E+SpRiQ==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.226.0.tgz", - "integrity": "sha512-m9gtLrrYnpN6yckcQ09rV7ExWOLMuq8mMPF/K3DbL/YL0TuILu9i2T1W+JuxSX+K9FMG2HrLAKivE/kMLr55xA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.303.0.tgz", + "integrity": "sha512-y2sqmmBdm4gXUL4SyN+ucfO/sxtOEDj2sB12ArRpDGyerfNLhAf7xpL4lXkjPx/7wTIjlBWoO2G/yK6t00P6fA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.226.0.tgz", - "integrity": "sha512-mwRbdKEUeuNH5TEkyZ5FWxp6bL2UC1WbY+LDv6YjHxmSMKpAoOueEdtU34PqDOLrpXXxIGHDFmjeGeMfktyEcA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.303.0.tgz", + "integrity": "sha512-z3MTsZMtPg6hYWl6a0o07q7zgsDXPYeP14XFVMc8NXqiAyNcm/OYwanpXyNjsEKI/X0nlpJ/Rs+IRCbaIqV9Mw==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-retry": { - "version": "3.235.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.235.0.tgz", - "integrity": "sha512-50WHbJGpD3SNp9763MAlHqIhXil++JdQbKejNpHg7HsJne/ao3ub+fDOfx//mMBjpzBV25BGd5UlfL6blrClSg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.303.0.tgz", + "integrity": "sha512-wxlqrdGOrCm2Jsra7YyfLyO34YRB/FNlXzwuJiZkqoAb/40ZAuFcWqDv41SP44y8liFXqfsMGuywJ7mK2cHvnA==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/service-error-classification": "3.229.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "tslib": "^2.3.1", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/service-error-classification": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-middleware": "3.303.0", + "@aws-sdk/util-retry": "3.303.0", + "tslib": "^2.5.0", "uuid": "^8.3.2" }, "engines": { @@ -743,439 +735,438 @@ } }, "node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.226.0.tgz", - "integrity": "sha512-NN9T/qoSD1kZvAT+VLny3NnlqgylYQcsgV3rvi/8lYzw/G/2s8VS6sm/VTWGGZhx08wZRv20MWzYu3bftcyqUg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.303.0.tgz", + "integrity": "sha512-igp7htNCUPhVL9Q6rJSgcx3qy/P2l2KAiS0oozOTaTXt3h0LbOusSXtwyA7qvLYeRthnw6msVW+rVBAW3Vo+3g==", "optional": true, "dependencies": { - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-signing": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-serde": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.226.0.tgz", - "integrity": "sha512-nPuOOAkSfx9TxzdKFx0X2bDlinOxGrqD7iof926K/AEflxGD1DBdcaDdjlYlPDW2CVE8LV/rAgbYuLxh/E/1VA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.303.0.tgz", + "integrity": "sha512-mmZozwYKgUgXkJrLVqgIYoOQ8DfKZS3pBBT3ZxWzv5Hz5M3oRqFgfVYljkeDM2CTvBweHpqVRTWqPDMcZisucg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-signing": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.226.0.tgz", - "integrity": "sha512-E6HmtPcl+IjYDDzi1xI2HpCbBq2avNWcjvCriMZWuTAtRVpnA6XDDGW5GY85IfS3A8G8vuWqEVPr8JcYUcjfew==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.303.0.tgz", + "integrity": "sha512-rrLQcS2wFsUGj9Kyx78LRgRS8jwiixz/Nyv06SmcKhP680sweETpQz/EA+wcVEVRXmUI6vs4NtqXz36dU0X8Nw==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/signature-v4": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-middleware": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-stack": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.226.0.tgz", - "integrity": "sha512-85wF29LvPvpoed60fZGDYLwv1Zpd/cM0C22WSSFPw1SSJeqO4gtFYyCg2squfT3KI6kF43IIkOCJ+L7GtryPug==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.303.0.tgz", + "integrity": "sha512-6KmdroXLexzILGxF/Xq0cGBs+B8Ipm1pff8qnWCT6KldYp+Q40bVcJrExkVHDN1uOsOxu20ixW2yujOKS356zg==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.226.0.tgz", - "integrity": "sha512-N1WnfzCW1Y5yWhVAphf8OPGTe8Df3vmV7/LdsoQfmpkCZgLZeK2o0xITkUQhRj1mbw7yp8tVFLFV3R2lMurdAQ==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.303.0.tgz", + "integrity": "sha512-ZVMVNxPRn2jXog3V4xWokSYoQxTKAdKlNoCfjqFplsF70r8sXfgZtOMF5ZhGo+Hgsx7GqpR/NWPKJtZD2nigpg==", "optional": true, "dependencies": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-endpoints": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/node-config-provider": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.226.0.tgz", - "integrity": "sha512-B8lQDqiRk7X5izFEUMXmi8CZLOKCTWQJU9HQf3ako+sF0gexo4nHN3jhoRWyLtcgC5S3on/2jxpAcqtm7kuY3w==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.303.0.tgz", + "integrity": "sha512-Ywbo9+2SkbdmNgCoxYJrv+YrFDtBH7hHtn2ywtzP4t57d4t0V/LNrNQsrAsXxqy48OS5r2ovOLHiqJS5jp1oyw==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/node-http-handler": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.226.0.tgz", - "integrity": "sha512-xQCddnZNMiPmjr3W7HYM+f5ir4VfxgJh37eqZwX6EZmyItFpNNeVzKUgA920ka1VPz/ZUYB+2OFGiX3LCLkkaA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.303.0.tgz", + "integrity": "sha512-5Te+mwBIOiQr2nM7/SNVFkvYHOH/CswOmUMV4Gxc7YjuervhrYvVFs2P+lL+c8rfiVMTLWjnJ6JiL2JdJfYgnQ==", "optional": true, "dependencies": { - "@aws-sdk/abort-controller": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/abort-controller": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/querystring-builder": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/property-provider": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.226.0.tgz", - "integrity": "sha512-TsljjG+Sg0LmdgfiAlWohluWKnxB/k8xenjeozZfzOr5bHmNHtdbWv6BtNvD/R83hw7SFXxbJHlD5H4u9p2NFg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.303.0.tgz", + "integrity": "sha512-d1qbn0pCz+jvB0dcWMWuIlWYM8dWCg3185ngMgUQxkgUk7/kEbwGBsmT+xtZAMQcwcgPkSm8qeATEQ7ToiH8eQ==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/protocol-http": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.226.0.tgz", - "integrity": "sha512-zWkVqiTA9RXL6y0hhfZc9bcU4DX2NI6Hw9IhQmSPeM59mdbPjJlY4bLlMr5YxywqO3yQ/ylNoAfrEzrDjlOSRg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.303.0.tgz", + "integrity": "sha512-eqblSsdmKBzgNl06dUnL4toq/OQgZyxVsxHCz2nI/xBk5lI/qAZIJyEgP2GmP8aoWwneAq33roG0VLZoxQ8exg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/querystring-builder": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.226.0.tgz", - "integrity": "sha512-LVurypuNeotO4lmirKXRC4NYrZRAyMJXuwO0f2a5ZAUJCjauwYrifKue6yCfU7bls7gut7nfcR6B99WBYpHs3g==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.303.0.tgz", + "integrity": "sha512-0eMp2gd7Ro0svJ6YVnp9cUiGtrc1d/HynyMfbDkLkqWJAnHMz7Oc1GjK5YyL1hdxm0W+JWZCPR0SovLiaboKDw==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-uri-escape": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/querystring-parser": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.226.0.tgz", - "integrity": "sha512-FzB+VrQ47KAFxiPt2YXrKZ8AOLZQqGTLCKHzx4bjxGmwgsjV8yIbtJiJhZLMcUQV4LtGeIY9ixIqQhGvnZHE4A==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.303.0.tgz", + "integrity": "sha512-KNJSQiTFiA7W5eYCox8bLGM7kghC3Azad86HQhdsYO0jCoPxcgj8MeP6T7fPTIC4WcTwcWb7T1MpzoeBiKMOTQ==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/service-error-classification": { - "version": "3.229.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.229.0.tgz", - "integrity": "sha512-dnzWWQ0/NoWMUZ5C0DW3dPm0wC1O76Y/SpKbuJzWPkx1EYy6r8p32Ly4D9vUzrKDbRGf48YHIF2kOkBmu21CLg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.303.0.tgz", + "integrity": "sha512-eO13PzdtRO9C+g3tyFOpIblX2SbDrIbg2bNtB8JOfjVi3E1b5VsSTXXU/cKV+lbZ9XMzMn3VzGSvpo6AjzfpxA==", "optional": true, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/shared-ini-file-loader": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.226.0.tgz", - "integrity": "sha512-661VQefsARxVyyV2FX9V61V+nNgImk7aN2hYlFKla6BCwZfMng+dEtD0xVGyg1PfRw0qvEv5LQyxMVgHcUSevA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.303.0.tgz", + "integrity": "sha512-yI84mnnh3pdQtIOo+oGWofaI0rvfhp3DOavB8KHIkQr+RcjF+fxsqbelRfVb25gx7yEWPNCMB8wM+HhklSEFJg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/signature-v4": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.226.0.tgz", - "integrity": "sha512-/R5q5agdPd7HJB68XMzpxrNPk158EHUvkFkuRu5Qf3kkkHebEzWEBlWoVpUe6ss4rP9Tqcue6xPuaftEmhjpYw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.303.0.tgz", + "integrity": "sha512-muw5yclLOgXPHIxv60mhO6R0GVjKbf+M6E/cWvIEVGq8Ke+mLMYNFYNdKP/f/8JgTtW2xwQ7pIK3U8x284ZqPw==", "optional": true, "dependencies": { - "@aws-sdk/is-array-buffer": "3.201.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-hex-encoding": "3.201.0", - "@aws-sdk/util-middleware": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/is-array-buffer": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-hex-encoding": "3.295.0", + "@aws-sdk/util-middleware": "3.303.0", + "@aws-sdk/util-uri-escape": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/smithy-client": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.234.0.tgz", - "integrity": "sha512-8AtR/k4vsFvjXeQbIzq/Wy7Nbk48Ou0wUEeVYPHWHPSU8QamFWORkOwmKtKMfHAyZvmqiAPeQqHFkq+UJhWyyQ==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.303.0.tgz", + "integrity": "sha512-WDTC9ODdpRAXo8+Mtr5hsPJeR3y3LxfZZFg5dplJgkaxV+MFdnsUCxZfAZMnxcGy5Q2qTzlLLNk9CpadS72v+g==", "optional": true, "dependencies": { - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-stack": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.238.0.tgz", - "integrity": "sha512-vYUwmy0kTzA99mJCVvad+/5RDlWve/xxnppT8DJK3JIdAgskp+rULY+joVnq2NSl489UAioUnFGs57vUxi8Pog==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.303.0.tgz", + "integrity": "sha512-7G7VYbqyX0v6RTD/m7XmArZToMek4jYXR/TuuGHK6ifNJeMDwkU4BcoVDj37vvTPYp6qKU5IE+bE3XmPyVWnGQ==", "optional": true, "dependencies": { - "@aws-sdk/client-sso-oidc": "3.238.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso-oidc": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/types": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.226.0.tgz", - "integrity": "sha512-MmmNHrWeO4man7wpOwrAhXlevqtOV9ZLcH4RhnG5LmRce0RFOApx24HoKENfFCcOyCm5LQBlsXCqi0dZWDWU0A==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.303.0.tgz", + "integrity": "sha512-H+Cy8JDTsK87MID6MJbV9ad5xdS9YvaLZSeveC2Zs1WNu2Rp6X9j+mg3EqDSmBKUQVAFRy2b+CSKkH3nnBMedw==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/url-parser": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.226.0.tgz", - "integrity": "sha512-p5RLE0QWyP0OcTOLmFcLdVgUcUEzmEfmdrnOxyNzomcYb0p3vUagA5zfa1HVK2azsQJFBv28GfvMnba9bGhObg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.303.0.tgz", + "integrity": "sha512-PXMXGhr89s0MiPTf8Ft/v3sPzh2geSrFhTVSO/01blfBQqtuu0JMqORhLheOdi16AhQNVlYHDW2tWdx7/T+KsA==", "optional": true, "dependencies": { - "@aws-sdk/querystring-parser": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/querystring-parser": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/util-base64": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.208.0.tgz", - "integrity": "sha512-PQniZph5A6N7uuEOQi+1hnMz/FSOK/8kMFyFO+4DgA1dZ5pcKcn5wiFwHkcTb/BsgVqQa3Jx0VHNnvhlS8JyTg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.303.0.tgz", + "integrity": "sha512-oj+p/GHHPcZEKjiiOHU/CyNQeh8i+8dfMMzU+VGdoK5jHaVG8h2b+V7GPf7I4wDkG2ySCK5b5Jw5NUHwdTJ13Q==", "optional": true, "dependencies": { - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-body-length-browser": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.188.0.tgz", - "integrity": "sha512-8VpnwFWXhnZ/iRSl9mTf+VKOX9wDE8QtN4bj9pBfxwf90H1X7E8T6NkiZD3k+HubYf2J94e7DbeHs7fuCPW5Qg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.303.0.tgz", + "integrity": "sha512-T643m0pKzgjAvPFy4W8zL+aszG3T22U8hb6stlMvT0z++Smv8QfIvkIkXjWyH2KlOt5GKliHwdOv8SAi0FSMJQ==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/util-body-length-node": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.208.0.tgz", - "integrity": "sha512-3zj50e5g7t/MQf53SsuuSf0hEELzMtD8RX8C76f12OSRo2Bca4FLLYHe0TZbxcfQHom8/hOaeZEyTyMogMglqg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.303.0.tgz", + "integrity": "sha512-/hS8z6e18Le60hJr2TUIFoUjUiAsnQsuDn6DxX74GXhMOHeSwZDJ9jHF39quYkNMmAE37GrVH4MI9vE0pN27qw==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-buffer-from": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.208.0.tgz", - "integrity": "sha512-7L0XUixNEFcLUGPeBF35enCvB9Xl+K6SQsmbrPk1P3mlV9mguWSDQqbOBwY1Ir0OVbD6H/ZOQU7hI/9RtRI0Zw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.303.0.tgz", + "integrity": "sha512-hUU+NW+SW6RNojtAKnnmz+tDShVKlEx2YsS4a5fSfrKRUes+zWz10cxVX0RQfysd3R6tdSHhbjsSj8eCIybheg==", "optional": true, "dependencies": { - "@aws-sdk/is-array-buffer": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/is-array-buffer": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-config-provider": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.208.0.tgz", - "integrity": "sha512-DSRqwrERUsT34ug+anlMBIFooBEGwM8GejC7q00Y/9IPrQy50KnG5PW2NiTjuLKNi7pdEOlwTSEocJE15eDZIg==", + "version": "3.295.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.295.0.tgz", + "integrity": "sha512-/5Dl1aV2yI8YQjqwmg4RTnl/E9NmNsx7HIwBZt+dTcOrM0LMUwczQBFFcLyqCj/qv5y+VsvLoAAA/OiBT7hb3w==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-defaults-mode-browser": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.234.0.tgz", - "integrity": "sha512-IHMKXjTbOD8XMz5+2oCOsVP94BYb9YyjXdns0aAXr2NAo7k2+RCzXQ2DebJXppGda1F6opFutoKwyVSN0cmbMw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.303.0.tgz", + "integrity": "sha512-jtZgCKelFe4/SHDHQu9ydbYttxSfqSlQojA5qxTJxLvzryIB+/GTHQ+sYWyMyzaD489W9elt1/cSsXd4LtPK0A==", "optional": true, "dependencies": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">= 10.0.0" } }, "node_modules/@aws-sdk/util-defaults-mode-node": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.234.0.tgz", - "integrity": "sha512-UGjQ+OjBYYhxFVtUY+jtr0ZZgzZh6OHtYwRhFt8IHewJXFCfZTyfsbX20szBj5y1S4HRIUJ7cwBLIytTqMbI5w==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.303.0.tgz", + "integrity": "sha512-c86iyot/u9bCVcy/rlWL+0kdR51c7C2d2yDXvO9iFCdMKAs28Hw1ijGczVmOcUQ61zKNFSGYx+VekHXN9IWYOg==", "optional": true, "dependencies": { - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/config-resolver": "3.303.0", + "@aws-sdk/credential-provider-imds": "3.303.0", + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">= 10.0.0" } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.226.0.tgz", - "integrity": "sha512-iqOkac/zLmyPBUJd7SLN0PeZMkOmlGgD5PHmmekTClOkce2eUjK9SNX1PzL73aXPoPTyhg9QGLH8uEZEQ8YUzg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.303.0.tgz", + "integrity": "sha512-dPg9+l3VY3nclWFiWAVNWek5lQwgdtY8oRYOgCeyntce9FlNrPQgCRTVr36D0iQ0aNCs0GWzfjgL+rIdCF66/w==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-hex-encoding": { - "version": "3.201.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.201.0.tgz", - "integrity": "sha512-7t1vR1pVxKx0motd3X9rI3m/xNp78p3sHtP5yo4NP4ARpxyJ0fokBomY8ScaH2D/B+U5o9ARxldJUdMqyBlJcA==", + "version": "3.295.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.295.0.tgz", + "integrity": "sha512-XJcoVo41kHzhe28PBm/rqt5mdCp8R6abwiW9ug1dA6FOoPUO8kBUxDv6xaOmA2hfRvd2ocFfBXaUCBqUowkGcQ==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.208.0.tgz", - "integrity": "sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg==", + "version": "3.295.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.295.0.tgz", + "integrity": "sha512-d/s+zhUx5Kh4l/ecMP/TBjzp1GR/g89Q4nWH6+wH5WgdHsK+LG+vmsk6mVNuP/8wsCofYG4NBqp5Ulbztbm9QA==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-middleware": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.226.0.tgz", - "integrity": "sha512-B96CQnwX4gRvQdaQkdUtqvDPkrptV5+va6FVeJOocU/DbSYMAScLxtR3peMS8cnlOT6nL1Eoa42OI9AfZz1VwQ==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.303.0.tgz", + "integrity": "sha512-HAfBcbZw1+pY3dIEDM4jVpH1ViFcGH5s0q1dr+x4rcLGpMM3B4dH0HUgDPtycG8sw+nk+9jGgiEtgaCNOpJLGA==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-retry": { - "version": "3.229.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.229.0.tgz", - "integrity": "sha512-0zKTqi0P1inD0LzIMuXRIYYQ/8c1lWMg/cfiqUcIAF1TpatlpZuN7umU0ierpBFud7S+zDgg0oemh+Nj8xliJw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.303.0.tgz", + "integrity": "sha512-RWwRNjoWMcpDouz69wPuFXWFVzwYtUkTbJfa46SjKl1IwqMHS4f9yjJfCwJIoLOW9M/o2JB7nD0Ij3gqqzajLw==", "optional": true, "dependencies": { - "@aws-sdk/service-error-classification": "3.229.0", - "tslib": "^2.3.1" + "@aws-sdk/service-error-classification": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@aws-sdk/util-uri-escape": { - "version": "3.201.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.201.0.tgz", - "integrity": "sha512-TeTWbGx4LU2c5rx0obHeDFeO9HvwYwQtMh1yniBz00pQb6Qt6YVOETVQikRZ+XRQwEyCg/dA375UplIpiy54mA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.303.0.tgz", + "integrity": "sha512-N3ULNuHCL3QzAlCTY+XRRkRQTYCTU8RRuzFCJX0pDpz9t2K+tLT7DbxqupWGNFGl5Xlulf1Is14J3BP/Dx91rA==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.226.0.tgz", - "integrity": "sha512-PhBIu2h6sPJPcv2I7ELfFizdl5pNiL4LfxrasMCYXQkJvVnoXztHA1x+CQbXIdtZOIlpjC+6BjDcE0uhnpvfcA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.303.0.tgz", + "integrity": "sha512-Kex3abpUrTX9z129jiI8sfjIUmQDwiWjhkvBkPmrwjFY/sZcnOcXj5nP2iwJ+k6CnA5ZK5PjZ6P62t+eJ5MTXw==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.226.0", + "@aws-sdk/types": "3.303.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.226.0.tgz", - "integrity": "sha512-othPc5Dz/pkYkxH+nZPhc1Al0HndQT8zHD4e9h+EZ+8lkd8n+IsnLfTS/mSJWrfiC6UlNRVw55cItstmJyMe/A==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.303.0.tgz", + "integrity": "sha512-QYUg8F/Ho6AsVZaSSRMf/LWoEPDyOwgKZBw3AbKoH6RxAdAsdL1SXz5t4A6jHakP9TLVN2Yw2WRbHDe4LATASQ==", "optional": true, "dependencies": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" @@ -1189,26 +1180,26 @@ } } }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.188.0.tgz", - "integrity": "sha512-jt627x0+jE+Ydr9NwkFstg3cUvgWh56qdaqAMDsqgRlKD21md/6G226z/Qxl7lb1VEW2LlmCx43ai/37Qwcj2Q==", + "node_modules/@aws-sdk/util-utf8": { + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.303.0.tgz", + "integrity": "sha512-tZXVuMOIONPOuOGBs/XRdzxv6jUvTM620dRFFIHZwlGiW8bo0x0LlonrzDAJZA4e9ZwmxJIj8Ji13WVRBGvZWg==", "optional": true, "dependencies": { - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.303.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/util-utf8-node": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.208.0.tgz", - "integrity": "sha512-jKY87Acv0yWBdFxx6bveagy5FYjz+dtV8IPT7ay1E2WPWH1czoIdMAkc8tSInK31T6CRnHWkLZ1qYwCbgRfERQ==", + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", "optional": true, "dependencies": { - "@aws-sdk/util-buffer-from": "3.208.0", "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" } }, "node_modules/@babel/code-frame": { @@ -4077,16 +4068,16 @@ } }, "node_modules/@mikro-orm/core": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-5.6.2.tgz", - "integrity": "sha512-FDKJBL1gjT9d36E9dyC5S9OxoeEQzy7lIj/SpijU8BDDC6gpn27HJ00DLqG+gLdepXBYdDdf3B/ELi+OGxZKCw==", + "version": "5.6.16", + "resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-5.6.16.tgz", + "integrity": "sha512-JTrVS4Rb5uVKbf/d26Ni/YgJjMyoHapPEUklr4H+4c+6xlRXbfpPDN6o4ESWNXTc4ubwRGLMI1haMXg0bh6uVQ==", "dependencies": { "acorn-loose": "8.3.0", "acorn-walk": "8.2.0", "dotenv": "16.0.3", - "fs-extra": "11.1.0", + "fs-extra": "11.1.1", "globby": "11.1.0", - "mikro-orm": "~5.6.2", + "mikro-orm": "~5.6.16", "reflect-metadata": "0.1.13" }, "engines": { @@ -4138,9 +4129,9 @@ } }, "node_modules/@mikro-orm/mongodb": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@mikro-orm/mongodb/-/mongodb-5.6.2.tgz", - "integrity": "sha512-z3dE0Bi2EgqCIZUnIzQyC1dlpBv3AmoUwvmmhvVmoz44avY0ZkZiXA/RFPocwvL9DZzKLI5zjMTj7dX9KPSyXw==", + "version": "5.6.16", + "resolved": "https://registry.npmjs.org/@mikro-orm/mongodb/-/mongodb-5.6.16.tgz", + "integrity": "sha512-MH7B4+OkQedLkv+ecTDRD41La8255LfpCuuwHNOnE8+XjF0tCXiD1XO4Cpcau98ubLR959HRs88OtulQwzgCQw==", "dependencies": { "bson": "^4.7.0", "mongodb": "4.13.0" @@ -5447,50 +5438,108 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@reldens/items-system": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@reldens/items-system/-/items-system-0.16.3.tgz", - "integrity": "sha512-8n/VcvdZjInPWRIsEqkGbDinrcrnFonVTzM87O/S9VfGfCWMzsuer76cUL5dCiOM6PNv5Fb5UVzWvFixv0S1CQ==", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@reldens/items-system/-/items-system-0.18.1.tgz", + "integrity": "sha512-gTKCb/Y2THC+Gd0V1IO8HiYHAk5oOvJMcIXCe/NX8TVrFs1Rr2mkWvMa30fBB37cei39zXGaiBdza1sSam3zWQ==", "dependencies": { - "@reldens/modifiers": "^0.17.1", - "@reldens/storage": "^0.12.2", - "@reldens/utils": "^0.17.7" + "@reldens/modifiers": "^0.19.0", + "@reldens/storage": "^0.13.0", + "@reldens/utils": "^0.19.0" + } + }, + "node_modules/@reldens/items-system/node_modules/@reldens/storage": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.13.0.tgz", + "integrity": "sha512-b9iuTk+szM7I9RGEcXuhK295EBhlqQ1K8LjUCvdJwsEHrxuz14EYCMZppykg8FEh1LYO1LCWQrOuywOPf7oEtg==", + "dependencies": { + "@mikro-orm/core": "^5.6.7", + "@mikro-orm/mongodb": "^5.6.7", + "@reldens/utils": "^0.18.0", + "knex": "^2.4.1", + "mysql": "^2.18.1", + "objection": "^3.0.1" + } + }, + "node_modules/@reldens/items-system/node_modules/@reldens/storage/node_modules/@reldens/utils": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.18.1.tgz", + "integrity": "sha512-ysWT+grnzdkrISMFsXvMBOMhknoIp9EvKkDMuUSB0zl+KzeEULqGcMz1B0MBXPbULVNz70DBExqVNKxFJUXWvg==", + "dependencies": { + "await-event-emitter": "^2.0.2" + } + }, + "node_modules/@reldens/items-system/node_modules/@reldens/utils": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.19.0.tgz", + "integrity": "sha512-GhRQaOotANHyAmDu6so1OJQwSyzmtFymzQXPQt8yFweRqCJudOtNxq2pYd9C5fN7NGC5W6w+lXUbhjSN2erpUA==", + "dependencies": { + "await-event-emitter": "^2.0.2" } }, "node_modules/@reldens/modifiers": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@reldens/modifiers/-/modifiers-0.17.1.tgz", - "integrity": "sha512-zPEVGv5Q2C9RKJZcMC1afXMVadKJ97b47wELxXwlaDsD4AEuc1nQiJrHUZcC7maZtiOtfxHrbasO8u2/K3GMnA==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@reldens/modifiers/-/modifiers-0.19.1.tgz", + "integrity": "sha512-WBN2WCxszwa00tVgGWqwcYeEuF7NrN1FkETWT2Q6lFSwbkIpVMxuwcz9gXsSJHG0fvPZHBfIlgYsHxc45hQO6Q==", "dependencies": { - "@reldens/utils": "^0.17.7" + "@reldens/utils": "^0.20.0" } }, "node_modules/@reldens/skills": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@reldens/skills/-/skills-0.16.6.tgz", - "integrity": "sha512-UfqmZqRAodaWTq/Qzm6tuxHi81jeYlAfYElbtn/XDl/qCHO75O/enXDMy2hUndl3G+A1w8DLmm2ziFERm3zDrg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@reldens/skills/-/skills-0.18.0.tgz", + "integrity": "sha512-pHPeF+1W7GJyUp0ZJ4lshBNbV+ec6uyW24KMequIhfmvQnRmBbhplV1VjRkqFBDiKLKHjbO4tSBzC0nj0UWoeg==", "dependencies": { - "@reldens/modifiers": "^0.17.1", - "@reldens/storage": "^0.12.2", - "@reldens/utils": "^0.17.8" + "@reldens/modifiers": "^0.19.0", + "@reldens/storage": "^0.13.0", + "@reldens/utils": "^0.19.0" + } + }, + "node_modules/@reldens/skills/node_modules/@reldens/storage": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.13.0.tgz", + "integrity": "sha512-b9iuTk+szM7I9RGEcXuhK295EBhlqQ1K8LjUCvdJwsEHrxuz14EYCMZppykg8FEh1LYO1LCWQrOuywOPf7oEtg==", + "dependencies": { + "@mikro-orm/core": "^5.6.7", + "@mikro-orm/mongodb": "^5.6.7", + "@reldens/utils": "^0.18.0", + "knex": "^2.4.1", + "mysql": "^2.18.1", + "objection": "^3.0.1" + } + }, + "node_modules/@reldens/skills/node_modules/@reldens/storage/node_modules/@reldens/utils": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.18.1.tgz", + "integrity": "sha512-ysWT+grnzdkrISMFsXvMBOMhknoIp9EvKkDMuUSB0zl+KzeEULqGcMz1B0MBXPbULVNz70DBExqVNKxFJUXWvg==", + "dependencies": { + "await-event-emitter": "^2.0.2" + } + }, + "node_modules/@reldens/skills/node_modules/@reldens/utils": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.19.0.tgz", + "integrity": "sha512-GhRQaOotANHyAmDu6so1OJQwSyzmtFymzQXPQt8yFweRqCJudOtNxq2pYd9C5fN7NGC5W6w+lXUbhjSN2erpUA==", + "dependencies": { + "await-event-emitter": "^2.0.2" } }, "node_modules/@reldens/storage": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.12.2.tgz", - "integrity": "sha512-lpgCCnaEnmgud/4OS7vF+LRvDtcH8RgD6cbqZULNHFN6mGLS/fwzCEBAaQqIIC9xxHarwbNxTSgNFpM4S+mfMA==", - "dependencies": { - "@mikro-orm/core": "^5.6.1", - "@mikro-orm/mongodb": "^5.6.1", - "@reldens/utils": "^0.17.7", - "knex": "^2.3.0", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.15.0.tgz", + "integrity": "sha512-xCzFREU0IxmSdnSsYjOp8ym2X5G/MfTFw0NWtfoYWLHJ17RbN9x3KpnCBMkllJzkb19tGUO3pqX1L9bocgSDgg==", + "dependencies": { + "@mikro-orm/core": "^5.6.16", + "@mikro-orm/mongodb": "^5.6.16", + "@reldens/utils": "^0.20.0", + "knex": "^2.4.2", "mysql": "^2.18.1", "objection": "^3.0.1" } }, "node_modules/@reldens/utils": { - "version": "0.17.8", - "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.17.8.tgz", - "integrity": "sha512-f6ssPTlFaji4tTWERxx+1Cz5zu13o23pmK75Xk0jxauk2kcq9JclwHcTPl8C3ojzQCHVhST1gBTeUOAVwLZ7OQ==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.20.0.tgz", + "integrity": "sha512-WzipJU4yoH68mVWh1+oTEI7ZVoxRTLli81ZI74v1KK16zihFG3YoJ5TIIQg+7BJMTjEkUXj3+PoEGRTON8KjLQ==", "dependencies": { "await-event-emitter": "^2.0.2" } @@ -6486,9 +6535,9 @@ } }, "node_modules/bson": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.0.tgz", - "integrity": "sha512-VrlEE4vuiO1WTpfof4VmaVolCVYkYTgB9iWgYNOrVlnifpME/06fhFRmONgBhClD5pFC1t9ZWqFUQEQAzY43bA==", + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", "dependencies": { "buffer": "^5.6.0" }, @@ -7671,9 +7720,9 @@ "optional": true }, "node_modules/fast-xml-parser": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz", - "integrity": "sha512-4aUg3aNRR/WjQAcpceODG1C3x3lFANXRo8+1biqfieHmg9pyMt7qB4lQV/Ta6sJCTbA5vfD8fnA8S54JATiFUA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz", + "integrity": "sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==", "optional": true, "dependencies": { "strnum": "^1.0.5" @@ -7687,9 +7736,9 @@ } }, "node_modules/fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dependencies": { "reusify": "^1.0.4" } @@ -7898,9 +7947,9 @@ } }, "node_modules/fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -8976,9 +9025,9 @@ } }, "node_modules/knex": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/knex/-/knex-2.3.0.tgz", - "integrity": "sha512-WMizPaq9wRMkfnwKXKXgBZeZFOSHGdtoSz5SaLAVNs3WRDfawt9O89T4XyH52PETxjV8/kRk0Yf+8WBEP/zbYw==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/knex/-/knex-2.4.2.tgz", + "integrity": "sha512-tMI1M7a+xwHhPxjbl/H9K1kHX+VncEYcvCx5K00M16bWvpYPKAZd6QrCu68PtHAdIZNQPWZn0GVhqVBEthGWCg==", "dependencies": { "colorette": "2.0.19", "commander": "^9.1.0", @@ -9417,9 +9466,9 @@ } }, "node_modules/mikro-orm": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-5.6.2.tgz", - "integrity": "sha512-LzKIAtA/Zj8qO1yKNRJLYI+Vywm1nBQqb89vPM7abZBnqqarGB89CWTlAt6DfaG9bNk7v+FK0atO5dqMEfLT1Q==", + "version": "5.6.16", + "resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-5.6.16.tgz", + "integrity": "sha512-HgG079qA5hWgGWlq9u3BjgE3ynGnDFsGRtvFhgo6W3Itkz46SsQ4oeQxRcAetd8mj/qM4SOLuy0k71pI6h0PkQ==", "engines": { "node": ">= 14.0.0" } @@ -12014,9 +12063,9 @@ } }, "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/type-fest": { "version": "0.20.2", @@ -12539,9 +12588,9 @@ } }, "@aws-crypto/ie11-detection": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.2.tgz", - "integrity": "sha512-5XDMQY98gMAf/WRTic5G++jfmS/VLM0rwpiOpaainKi4L0nqWMSB1SzsrEG5rjFZGYN6ZAefO+/Yta2dFM0kMw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", "optional": true, "requires": { "tslib": "^1.11.1" @@ -12556,16 +12605,16 @@ } }, "@aws-crypto/sha256-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", - "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", "optional": true, "requires": { - "@aws-crypto/ie11-detection": "^2.0.0", - "@aws-crypto/sha256-js": "^2.0.0", - "@aws-crypto/supports-web-crypto": "^2.0.0", - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" @@ -12580,13 +12629,13 @@ } }, "@aws-crypto/sha256-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", - "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", "optional": true, "requires": { - "@aws-crypto/util": "^2.0.0", - "@aws-sdk/types": "^3.1.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", "tslib": "^1.11.1" }, "dependencies": { @@ -12599,9 +12648,9 @@ } }, "@aws-crypto/supports-web-crypto": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.2.tgz", - "integrity": "sha512-6mbSsLHwZ99CTOOswvCRP3C+VCWnzBf+1SnbWxzzJ9lR0mA0JnY2JEAhp8rqmTE0GPFy88rrM27ffgp62oErMQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", "optional": true, "requires": { "tslib": "^1.11.1" @@ -12616,12 +12665,12 @@ } }, "@aws-crypto/util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.2.tgz", - "integrity": "sha512-Lgu5v/0e/BcrZ5m/IWqzPUf3UYFTy/PpeED+uc9SWUR1iZQL8XXbGQg10UfllwwBryO3hFF5dizK+78aoXC1eA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", "optional": true, "requires": { - "@aws-sdk/types": "^3.110.0", + "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" }, @@ -12635,812 +12684,803 @@ } }, "@aws-sdk/abort-controller": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.226.0.tgz", - "integrity": "sha512-cJVzr1xxPBd08voknXvR0RLgtZKGKt6WyDpH/BaPCu3rfSqWCDZKzwqe940eqosjmKrxC6pUZNKASIqHOQ8xxQ==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.303.0.tgz", + "integrity": "sha512-LzNzpeyTppcmV/6SAQI3T/huOkMrUnFveplgVNwJxw+rVqmqmGV6z6vpg+oRICRDcjXWYiSiaClxxSVvOy0sDQ==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-cognito-identity": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.238.0.tgz", - "integrity": "sha512-vjIVFzlkcUX9YT7pqg9NrvOTuqqsdYSesFgMLTCSWXBE5rPjWEK5ljIVOZtGn8MJQgwqS+Lqc8oB1vIfE+EaSA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.303.0.tgz", + "integrity": "sha512-rybplTjq6aj7DttT+v8ycajT8BIFXqdo66lkQjO1YykEIyVTnY4L9McTyNFOZsvNmG1LMSqb95/eYP463Lp7fg==", "optional": true, "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/client-sts": "3.238.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.238.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.303.0", + "@aws-sdk/config-resolver": "3.303.0", + "@aws-sdk/credential-provider-node": "3.303.0", + "@aws-sdk/fetch-http-handler": "3.303.0", + "@aws-sdk/hash-node": "3.303.0", + "@aws-sdk/invalid-dependency": "3.303.0", + "@aws-sdk/middleware-content-length": "3.303.0", + "@aws-sdk/middleware-endpoint": "3.303.0", + "@aws-sdk/middleware-host-header": "3.303.0", + "@aws-sdk/middleware-logger": "3.303.0", + "@aws-sdk/middleware-recursion-detection": "3.303.0", + "@aws-sdk/middleware-retry": "3.303.0", + "@aws-sdk/middleware-serde": "3.303.0", + "@aws-sdk/middleware-signing": "3.303.0", + "@aws-sdk/middleware-stack": "3.303.0", + "@aws-sdk/middleware-user-agent": "3.303.0", + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/node-http-handler": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/smithy-client": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "@aws-sdk/util-base64": "3.303.0", + "@aws-sdk/util-body-length-browser": "3.303.0", + "@aws-sdk/util-body-length-node": "3.303.0", + "@aws-sdk/util-defaults-mode-browser": "3.303.0", + "@aws-sdk/util-defaults-mode-node": "3.303.0", + "@aws-sdk/util-endpoints": "3.303.0", + "@aws-sdk/util-retry": "3.303.0", + "@aws-sdk/util-user-agent-browser": "3.303.0", + "@aws-sdk/util-user-agent-node": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-sso": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.238.0.tgz", - "integrity": "sha512-KHJJWP7hBDa9KLYiU5+hOb+3AAba93PhWebXkpKyQ/Bs+e7ECCreyLCwuME6uWTV01NDuFDpwZ6zUMpyNIcP6Q==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.303.0.tgz", + "integrity": "sha512-LZ+Z6vGnEdqmxx0dqtZP97n5VX5uUKu4lJmDR3sdGolxAUqCY1FxHDZd9DzCFXR8rwoJK4VJTL+exzeVp4Ly/g==", "optional": true, "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.303.0", + "@aws-sdk/fetch-http-handler": "3.303.0", + "@aws-sdk/hash-node": "3.303.0", + "@aws-sdk/invalid-dependency": "3.303.0", + "@aws-sdk/middleware-content-length": "3.303.0", + "@aws-sdk/middleware-endpoint": "3.303.0", + "@aws-sdk/middleware-host-header": "3.303.0", + "@aws-sdk/middleware-logger": "3.303.0", + "@aws-sdk/middleware-recursion-detection": "3.303.0", + "@aws-sdk/middleware-retry": "3.303.0", + "@aws-sdk/middleware-serde": "3.303.0", + "@aws-sdk/middleware-stack": "3.303.0", + "@aws-sdk/middleware-user-agent": "3.303.0", + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/node-http-handler": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/smithy-client": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "@aws-sdk/util-base64": "3.303.0", + "@aws-sdk/util-body-length-browser": "3.303.0", + "@aws-sdk/util-body-length-node": "3.303.0", + "@aws-sdk/util-defaults-mode-browser": "3.303.0", + "@aws-sdk/util-defaults-mode-node": "3.303.0", + "@aws-sdk/util-endpoints": "3.303.0", + "@aws-sdk/util-retry": "3.303.0", + "@aws-sdk/util-user-agent-browser": "3.303.0", + "@aws-sdk/util-user-agent-node": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-sso-oidc": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.238.0.tgz", - "integrity": "sha512-kazcA2Kp+cXQRtaZi5/T5YFfU9J3nzu1tXJsh0xAm+J3S9LS1ertY1bSX6KBed2xuxx2mfum8JRqli0TJad/pA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.303.0.tgz", + "integrity": "sha512-oOdDcBjxGiJ6mFWUMVr+A1hAzGRpcZ+oLAhCakpvpXCUG50PZSBFP+vOQXgHY/XNolqDg+IHq60oE9HoPzGleg==", "optional": true, "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "tslib": "^2.3.1" + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.303.0", + "@aws-sdk/fetch-http-handler": "3.303.0", + "@aws-sdk/hash-node": "3.303.0", + "@aws-sdk/invalid-dependency": "3.303.0", + "@aws-sdk/middleware-content-length": "3.303.0", + "@aws-sdk/middleware-endpoint": "3.303.0", + "@aws-sdk/middleware-host-header": "3.303.0", + "@aws-sdk/middleware-logger": "3.303.0", + "@aws-sdk/middleware-recursion-detection": "3.303.0", + "@aws-sdk/middleware-retry": "3.303.0", + "@aws-sdk/middleware-serde": "3.303.0", + "@aws-sdk/middleware-stack": "3.303.0", + "@aws-sdk/middleware-user-agent": "3.303.0", + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/node-http-handler": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/smithy-client": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "@aws-sdk/util-base64": "3.303.0", + "@aws-sdk/util-body-length-browser": "3.303.0", + "@aws-sdk/util-body-length-node": "3.303.0", + "@aws-sdk/util-defaults-mode-browser": "3.303.0", + "@aws-sdk/util-defaults-mode-node": "3.303.0", + "@aws-sdk/util-endpoints": "3.303.0", + "@aws-sdk/util-retry": "3.303.0", + "@aws-sdk/util-user-agent-browser": "3.303.0", + "@aws-sdk/util-user-agent-node": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/client-sts": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.238.0.tgz", - "integrity": "sha512-jQNwHqxWUGvWCN4o8KUFYQES8r41Oobu7x1KZOMrPhPxy27FUcDjBq/h85VoD2/AZlETSCZLiCnKV3KBh5pT5w==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.303.0.tgz", + "integrity": "sha512-oda7mOfGyJZe62DZ5BVH3L84yeDM0Ja/fSpTjwV9hqFqzgtW83TCpiNegcJmvmGWDYaPmE2qpfDPqPzymB0sBg==", "optional": true, "requires": { - "@aws-crypto/sha256-browser": "2.0.0", - "@aws-crypto/sha256-js": "2.0.0", - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-node": "3.238.0", - "@aws-sdk/fetch-http-handler": "3.226.0", - "@aws-sdk/hash-node": "3.226.0", - "@aws-sdk/invalid-dependency": "3.226.0", - "@aws-sdk/middleware-content-length": "3.226.0", - "@aws-sdk/middleware-endpoint": "3.226.0", - "@aws-sdk/middleware-host-header": "3.226.0", - "@aws-sdk/middleware-logger": "3.226.0", - "@aws-sdk/middleware-recursion-detection": "3.226.0", - "@aws-sdk/middleware-retry": "3.235.0", - "@aws-sdk/middleware-sdk-sts": "3.226.0", - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/middleware-user-agent": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/node-http-handler": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/smithy-client": "3.234.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "@aws-sdk/util-body-length-browser": "3.188.0", - "@aws-sdk/util-body-length-node": "3.208.0", - "@aws-sdk/util-defaults-mode-browser": "3.234.0", - "@aws-sdk/util-defaults-mode-node": "3.234.0", - "@aws-sdk/util-endpoints": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "@aws-sdk/util-user-agent-browser": "3.226.0", - "@aws-sdk/util-user-agent-node": "3.226.0", - "@aws-sdk/util-utf8-browser": "3.188.0", - "@aws-sdk/util-utf8-node": "3.208.0", - "fast-xml-parser": "4.0.11", - "tslib": "^2.3.1" + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.303.0", + "@aws-sdk/credential-provider-node": "3.303.0", + "@aws-sdk/fetch-http-handler": "3.303.0", + "@aws-sdk/hash-node": "3.303.0", + "@aws-sdk/invalid-dependency": "3.303.0", + "@aws-sdk/middleware-content-length": "3.303.0", + "@aws-sdk/middleware-endpoint": "3.303.0", + "@aws-sdk/middleware-host-header": "3.303.0", + "@aws-sdk/middleware-logger": "3.303.0", + "@aws-sdk/middleware-recursion-detection": "3.303.0", + "@aws-sdk/middleware-retry": "3.303.0", + "@aws-sdk/middleware-sdk-sts": "3.303.0", + "@aws-sdk/middleware-serde": "3.303.0", + "@aws-sdk/middleware-signing": "3.303.0", + "@aws-sdk/middleware-stack": "3.303.0", + "@aws-sdk/middleware-user-agent": "3.303.0", + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/node-http-handler": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/smithy-client": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "@aws-sdk/util-base64": "3.303.0", + "@aws-sdk/util-body-length-browser": "3.303.0", + "@aws-sdk/util-body-length-node": "3.303.0", + "@aws-sdk/util-defaults-mode-browser": "3.303.0", + "@aws-sdk/util-defaults-mode-node": "3.303.0", + "@aws-sdk/util-endpoints": "3.303.0", + "@aws-sdk/util-retry": "3.303.0", + "@aws-sdk/util-user-agent-browser": "3.303.0", + "@aws-sdk/util-user-agent-node": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "fast-xml-parser": "4.1.2", + "tslib": "^2.5.0" } }, "@aws-sdk/config-resolver": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.234.0.tgz", - "integrity": "sha512-uZxy4wzllfvgCQxVc+Iqhde0NGAnfmV2hWR6ejadJaAFTuYNvQiRg9IqJy3pkyDPqXySiJ8Bom5PoJfgn55J/A==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.303.0.tgz", + "integrity": "sha512-uGZ47jcH86AwWcjZjuOL5jK5qE4izrEol8oF7KY214kjmavbKQstyUqmcwL2lr/YpDNFkCYgUxWRpduqVm8zmw==", "optional": true, "requires": { - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-config-provider": "3.295.0", + "@aws-sdk/util-middleware": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-cognito-identity": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.238.0.tgz", - "integrity": "sha512-FGVXGtGn7t/XacBdCKgqzxJNvorjCIar+F0oRsGq8aH09L/H8uGZ7qXvYvi9I94Qv7ay0Dy/KpGcMB53zt41UA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.303.0.tgz", + "integrity": "sha512-9MYsGJCNLsm61PW/JFm4y0Cv6aluCkZmE5D/g4vYnEFOZSKyK15m1a10RKGAh391fh6Bg1kU9WOoqkGk3Nyqng==", "optional": true, "requires": { - "@aws-sdk/client-cognito-identity": "3.238.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-cognito-identity": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-env": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.226.0.tgz", - "integrity": "sha512-sd8uK1ojbXxaZXlthzw/VXZwCPUtU3PjObOfr3Evj7MPIM2IH8h29foOlggx939MdLQGboJf9gKvLlvKDWtJRA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.303.0.tgz", + "integrity": "sha512-rtXumfF4cGrVk9fWACeLCfdpmlzlDUkzwSR60/3enC5Antcxl3fFY5T1BzNFvz0mB0zcwm4kaAwIcljX67DNRA==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-imds": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.226.0.tgz", - "integrity": "sha512-//z/COQm2AjYFI1Lb0wKHTQSrvLFTyuKLFQGPJsKS7DPoxGOCKB7hmYerlbl01IDoCxTdyL//TyyPxbZEOQD5Q==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.303.0.tgz", + "integrity": "sha512-ruomcFkKUpJkZb87em698//A0AVpt1KN9dn8N8eVyOuvZzebVxSW4AJoVgOKd5Av4PVcZgEqRX0kOOVp0iTrWg==", "optional": true, "requires": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-ini": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.238.0.tgz", - "integrity": "sha512-WmPNtIYyUasjV7VQxvPNq7ihmx0vFsiKAtjNjjakdrt5TPoma4nUYb9tIG9SuG+kcp4DJIgRLJAgZtXbCcVimg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.303.0.tgz", + "integrity": "sha512-4J50F6fEjQmAstSQOpJFG+rnbEqtwA7nDG6PxNm98VSTH2mYJV0YgBdvydfBKrKINAT4xYZta5Sc4WEIpSo0TA==", "optional": true, "requires": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.238.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/credential-provider-env": "3.303.0", + "@aws-sdk/credential-provider-imds": "3.303.0", + "@aws-sdk/credential-provider-process": "3.303.0", + "@aws-sdk/credential-provider-sso": "3.303.0", + "@aws-sdk/credential-provider-web-identity": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-node": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.238.0.tgz", - "integrity": "sha512-/RN5EyGfgdIIJdFzv+O0nSaHX1/F3anQjTIBeVg8GJ+82m+bDxMdALsG+NzkYnLilN9Uhc1lSNjLBCoPa5DSEg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.303.0.tgz", + "integrity": "sha512-OlKb7O2jDtrzkzLT/PUb5kxuGGTIyPn2alXzGT+7LdJ9/tP8KlqSVMtnH2UYPPdcc/daK16+MRNL5ylxmnRJ7Q==", "optional": true, "requires": { - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.238.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.238.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/credential-provider-env": "3.303.0", + "@aws-sdk/credential-provider-imds": "3.303.0", + "@aws-sdk/credential-provider-ini": "3.303.0", + "@aws-sdk/credential-provider-process": "3.303.0", + "@aws-sdk/credential-provider-sso": "3.303.0", + "@aws-sdk/credential-provider-web-identity": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-process": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.226.0.tgz", - "integrity": "sha512-iUDMdnrTvbvaCFhWwqyXrhvQ9+ojPqPqXhwZtY1X/Qaz+73S9gXBPJHZaZb2Ke0yKE1Ql3bJbKvmmxC/qLQMng==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.303.0.tgz", + "integrity": "sha512-1pxDYRscGlERAjFE5hSF1KQdcyOGzssuRTdLvez4I/mSIOAJLMmBAnmHGI/DME2LzDVrC9dklA6LHSC2sn3quQ==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-sso": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.238.0.tgz", - "integrity": "sha512-i70V4bFlCVYey3QARJ6XxKEg/4YuoFRnePV2oK37UHOGpEn49uXKwVZqLjzJgFHln7BPlC06cWDqrHUQIMvYrQ==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.303.0.tgz", + "integrity": "sha512-/szzM1BzZGjHwV4mSiZo65cyDleJqnxM9Y4autg55mb3dFwcCiMGI6TGbdegumrNZZlCTeTA1lIhA9PdT4gDAQ==", "optional": true, "requires": { - "@aws-sdk/client-sso": "3.238.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/token-providers": "3.238.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/token-providers": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-provider-web-identity": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.226.0.tgz", - "integrity": "sha512-CCpv847rLB0SFOHz2igvUMFAzeT2fD3YnY4C8jltuJoEkn0ITn1Hlgt13nTJ5BUuvyti2mvyXZHmNzhMIMrIlw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.303.0.tgz", + "integrity": "sha512-qi5CP4ocseqdj3kMi0vgLx8XrdanLNvCAfgiEF6LjUJI88R2snZAYNUSd+Y2n04mKAalns+mUwfUN2JyL66d5g==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/credential-providers": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.238.0.tgz", - "integrity": "sha512-wqj88z9UCbqxnd9XoaKrI+7oSN/13f1AP7cZWYKrzdvb3Ae5QmXmgMKXJdsQlWtPijchpPOQNGhd1rVogycgiA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.303.0.tgz", + "integrity": "sha512-ueO8UKvYyzt2lexvIdg50TFC7EO2shRWbMWPsVi6Ul7euoQzthr/TPQts4OLZIt9XeIFd4s9dhFwYSobcRfVGw==", "optional": true, "requires": { - "@aws-sdk/client-cognito-identity": "3.238.0", - "@aws-sdk/client-sso": "3.238.0", - "@aws-sdk/client-sts": "3.238.0", - "@aws-sdk/credential-provider-cognito-identity": "3.238.0", - "@aws-sdk/credential-provider-env": "3.226.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/credential-provider-ini": "3.238.0", - "@aws-sdk/credential-provider-node": "3.238.0", - "@aws-sdk/credential-provider-process": "3.226.0", - "@aws-sdk/credential-provider-sso": "3.238.0", - "@aws-sdk/credential-provider-web-identity": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-cognito-identity": "3.303.0", + "@aws-sdk/client-sso": "3.303.0", + "@aws-sdk/client-sts": "3.303.0", + "@aws-sdk/credential-provider-cognito-identity": "3.303.0", + "@aws-sdk/credential-provider-env": "3.303.0", + "@aws-sdk/credential-provider-imds": "3.303.0", + "@aws-sdk/credential-provider-ini": "3.303.0", + "@aws-sdk/credential-provider-node": "3.303.0", + "@aws-sdk/credential-provider-process": "3.303.0", + "@aws-sdk/credential-provider-sso": "3.303.0", + "@aws-sdk/credential-provider-web-identity": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/fetch-http-handler": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.226.0.tgz", - "integrity": "sha512-JewZPMNEBXfi1xVnRa7pVtK/zgZD8/lQ/YnD8pq79WuMa2cwyhDtr8oqCoqsPW+WJT5ScXoMtuHxN78l8eKWgg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.303.0.tgz", + "integrity": "sha512-Bc6C86/KQOSWPa741h9QEVcApyignYV5vC5+zZjmKkcyPxrVxTmL3kTJidpVOtVfCmTIrNN/WhAVDzLBbh1ycQ==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-base64": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/querystring-builder": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-base64": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/hash-node": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.226.0.tgz", - "integrity": "sha512-MdlJhJ9/Espwd0+gUXdZRsHuostB2WxEVAszWxobP0FTT9PnicqnfK7ExmW+DUAc0ywxtEbR3e0UND65rlSTVw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.303.0.tgz", + "integrity": "sha512-jSo4A/JxTabZ9jHrx7nhKIXnOmvPg/SSYnoHaFdVS5URJrNt1w+nSvW1wLGMEMOvu5+NU3bldBBSb+h0Ocwv1A==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-buffer-from": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/invalid-dependency": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.226.0.tgz", - "integrity": "sha512-QXOYFmap8g9QzRjumcRCIo2GEZkdCwd7ePQW0OABWPhKHzlJ74vvBxywjU3s39EEBEluWXtZ7Iufg6GxZM4ifw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.303.0.tgz", + "integrity": "sha512-RXNcLxOrUJaMMqk5uIYEf6X9XCMockT27bS8Dde/0ms015VOo8Wn2hHU9wEmGeFvLccC2UU4gPzvmj74w70q2Q==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/is-array-buffer": { - "version": "3.201.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.201.0.tgz", - "integrity": "sha512-UPez5qLh3dNgt0DYnPD/q0mVJY84rA17QE26hVNOW3fAji8W2wrwrxdacWOxyXvlxWsVRcKmr+lay1MDqpAMfg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.303.0.tgz", + "integrity": "sha512-IitBTr+pou7v5BrYLFH/SbIf3g1LIgMhcI3bDXBq2FjzmDftj4bW8BOmg05b9YKf2TrrggvJ4yk/jH+yYFXoJQ==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-content-length": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.226.0.tgz", - "integrity": "sha512-ksUzlHJN2JMuyavjA46a4sctvnrnITqt2tbGGWWrAuXY1mel2j+VbgnmJUiwHKUO6bTFBBeft5Vd1TSOb4JmiA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.303.0.tgz", + "integrity": "sha512-0UL5TWSL1JRpjT6gjGsZXfia5oL7vxzj+CfMCqkP6gjVF69eRcgu426Xc6TJwDcr6jIFPeamDBTLyt9ZAAr6hg==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-endpoint": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.226.0.tgz", - "integrity": "sha512-EvLFafjtUxTT0AC9p3aBQu1/fjhWdIeK58jIXaNFONfZ3F8QbEYUPuF/SqZvJM6cWfOO9qwYKkRDbCSTYhprIg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.303.0.tgz", + "integrity": "sha512-z2i8LJ6YTKbqXh9rY/KbXihvhq6P0JVI6SnkwT2hesJp0Nfldx85jsaLzj1+ioNKlQ+51u9UmBnO404DgNCAbg==", "optional": true, "requires": { - "@aws-sdk/middleware-serde": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/url-parser": "3.226.0", - "@aws-sdk/util-config-provider": "3.208.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-serde": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/url-parser": "3.303.0", + "@aws-sdk/util-middleware": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-host-header": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.226.0.tgz", - "integrity": "sha512-haVkWVh6BUPwKgWwkL6sDvTkcZWvJjv8AgC8jiQuSl8GLZdzHTB8Qhi3IsfFta9HAuoLjxheWBE5Z/L0UrfhLA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.303.0.tgz", + "integrity": "sha512-LUyhtjbuosrD0QAsBZJwT3yp146I7Xjehf42OP3dWbRuklMEilI0Res5K2/nknf3/ZKUj6sf7BbJoU8E+SpRiQ==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-logger": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.226.0.tgz", - "integrity": "sha512-m9gtLrrYnpN6yckcQ09rV7ExWOLMuq8mMPF/K3DbL/YL0TuILu9i2T1W+JuxSX+K9FMG2HrLAKivE/kMLr55xA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.303.0.tgz", + "integrity": "sha512-y2sqmmBdm4gXUL4SyN+ucfO/sxtOEDj2sB12ArRpDGyerfNLhAf7xpL4lXkjPx/7wTIjlBWoO2G/yK6t00P6fA==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-recursion-detection": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.226.0.tgz", - "integrity": "sha512-mwRbdKEUeuNH5TEkyZ5FWxp6bL2UC1WbY+LDv6YjHxmSMKpAoOueEdtU34PqDOLrpXXxIGHDFmjeGeMfktyEcA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.303.0.tgz", + "integrity": "sha512-z3MTsZMtPg6hYWl6a0o07q7zgsDXPYeP14XFVMc8NXqiAyNcm/OYwanpXyNjsEKI/X0nlpJ/Rs+IRCbaIqV9Mw==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-retry": { - "version": "3.235.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.235.0.tgz", - "integrity": "sha512-50WHbJGpD3SNp9763MAlHqIhXil++JdQbKejNpHg7HsJne/ao3ub+fDOfx//mMBjpzBV25BGd5UlfL6blrClSg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.303.0.tgz", + "integrity": "sha512-wxlqrdGOrCm2Jsra7YyfLyO34YRB/FNlXzwuJiZkqoAb/40ZAuFcWqDv41SP44y8liFXqfsMGuywJ7mK2cHvnA==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/service-error-classification": "3.229.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "@aws-sdk/util-retry": "3.229.0", - "tslib": "^2.3.1", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/service-error-classification": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-middleware": "3.303.0", + "@aws-sdk/util-retry": "3.303.0", + "tslib": "^2.5.0", "uuid": "^8.3.2" } }, "@aws-sdk/middleware-sdk-sts": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.226.0.tgz", - "integrity": "sha512-NN9T/qoSD1kZvAT+VLny3NnlqgylYQcsgV3rvi/8lYzw/G/2s8VS6sm/VTWGGZhx08wZRv20MWzYu3bftcyqUg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.303.0.tgz", + "integrity": "sha512-igp7htNCUPhVL9Q6rJSgcx3qy/P2l2KAiS0oozOTaTXt3h0LbOusSXtwyA7qvLYeRthnw6msVW+rVBAW3Vo+3g==", "optional": true, "requires": { - "@aws-sdk/middleware-signing": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-signing": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-serde": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.226.0.tgz", - "integrity": "sha512-nPuOOAkSfx9TxzdKFx0X2bDlinOxGrqD7iof926K/AEflxGD1DBdcaDdjlYlPDW2CVE8LV/rAgbYuLxh/E/1VA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.303.0.tgz", + "integrity": "sha512-mmZozwYKgUgXkJrLVqgIYoOQ8DfKZS3pBBT3ZxWzv5Hz5M3oRqFgfVYljkeDM2CTvBweHpqVRTWqPDMcZisucg==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-signing": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.226.0.tgz", - "integrity": "sha512-E6HmtPcl+IjYDDzi1xI2HpCbBq2avNWcjvCriMZWuTAtRVpnA6XDDGW5GY85IfS3A8G8vuWqEVPr8JcYUcjfew==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.303.0.tgz", + "integrity": "sha512-rrLQcS2wFsUGj9Kyx78LRgRS8jwiixz/Nyv06SmcKhP680sweETpQz/EA+wcVEVRXmUI6vs4NtqXz36dU0X8Nw==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/signature-v4": "3.226.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-middleware": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/signature-v4": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-middleware": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-stack": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.226.0.tgz", - "integrity": "sha512-85wF29LvPvpoed60fZGDYLwv1Zpd/cM0C22WSSFPw1SSJeqO4gtFYyCg2squfT3KI6kF43IIkOCJ+L7GtryPug==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.303.0.tgz", + "integrity": "sha512-6KmdroXLexzILGxF/Xq0cGBs+B8Ipm1pff8qnWCT6KldYp+Q40bVcJrExkVHDN1uOsOxu20ixW2yujOKS356zg==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/middleware-user-agent": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.226.0.tgz", - "integrity": "sha512-N1WnfzCW1Y5yWhVAphf8OPGTe8Df3vmV7/LdsoQfmpkCZgLZeK2o0xITkUQhRj1mbw7yp8tVFLFV3R2lMurdAQ==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.303.0.tgz", + "integrity": "sha512-ZVMVNxPRn2jXog3V4xWokSYoQxTKAdKlNoCfjqFplsF70r8sXfgZtOMF5ZhGo+Hgsx7GqpR/NWPKJtZD2nigpg==", "optional": true, "requires": { - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-endpoints": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/node-config-provider": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.226.0.tgz", - "integrity": "sha512-B8lQDqiRk7X5izFEUMXmi8CZLOKCTWQJU9HQf3ako+sF0gexo4nHN3jhoRWyLtcgC5S3on/2jxpAcqtm7kuY3w==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.303.0.tgz", + "integrity": "sha512-Ywbo9+2SkbdmNgCoxYJrv+YrFDtBH7hHtn2ywtzP4t57d4t0V/LNrNQsrAsXxqy48OS5r2ovOLHiqJS5jp1oyw==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/node-http-handler": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.226.0.tgz", - "integrity": "sha512-xQCddnZNMiPmjr3W7HYM+f5ir4VfxgJh37eqZwX6EZmyItFpNNeVzKUgA920ka1VPz/ZUYB+2OFGiX3LCLkkaA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.303.0.tgz", + "integrity": "sha512-5Te+mwBIOiQr2nM7/SNVFkvYHOH/CswOmUMV4Gxc7YjuervhrYvVFs2P+lL+c8rfiVMTLWjnJ6JiL2JdJfYgnQ==", "optional": true, "requires": { - "@aws-sdk/abort-controller": "3.226.0", - "@aws-sdk/protocol-http": "3.226.0", - "@aws-sdk/querystring-builder": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/abort-controller": "3.303.0", + "@aws-sdk/protocol-http": "3.303.0", + "@aws-sdk/querystring-builder": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/property-provider": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.226.0.tgz", - "integrity": "sha512-TsljjG+Sg0LmdgfiAlWohluWKnxB/k8xenjeozZfzOr5bHmNHtdbWv6BtNvD/R83hw7SFXxbJHlD5H4u9p2NFg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.303.0.tgz", + "integrity": "sha512-d1qbn0pCz+jvB0dcWMWuIlWYM8dWCg3185ngMgUQxkgUk7/kEbwGBsmT+xtZAMQcwcgPkSm8qeATEQ7ToiH8eQ==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/protocol-http": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.226.0.tgz", - "integrity": "sha512-zWkVqiTA9RXL6y0hhfZc9bcU4DX2NI6Hw9IhQmSPeM59mdbPjJlY4bLlMr5YxywqO3yQ/ylNoAfrEzrDjlOSRg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.303.0.tgz", + "integrity": "sha512-eqblSsdmKBzgNl06dUnL4toq/OQgZyxVsxHCz2nI/xBk5lI/qAZIJyEgP2GmP8aoWwneAq33roG0VLZoxQ8exg==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/querystring-builder": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.226.0.tgz", - "integrity": "sha512-LVurypuNeotO4lmirKXRC4NYrZRAyMJXuwO0f2a5ZAUJCjauwYrifKue6yCfU7bls7gut7nfcR6B99WBYpHs3g==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.303.0.tgz", + "integrity": "sha512-0eMp2gd7Ro0svJ6YVnp9cUiGtrc1d/HynyMfbDkLkqWJAnHMz7Oc1GjK5YyL1hdxm0W+JWZCPR0SovLiaboKDw==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-uri-escape": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/querystring-parser": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.226.0.tgz", - "integrity": "sha512-FzB+VrQ47KAFxiPt2YXrKZ8AOLZQqGTLCKHzx4bjxGmwgsjV8yIbtJiJhZLMcUQV4LtGeIY9ixIqQhGvnZHE4A==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.303.0.tgz", + "integrity": "sha512-KNJSQiTFiA7W5eYCox8bLGM7kghC3Azad86HQhdsYO0jCoPxcgj8MeP6T7fPTIC4WcTwcWb7T1MpzoeBiKMOTQ==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/service-error-classification": { - "version": "3.229.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.229.0.tgz", - "integrity": "sha512-dnzWWQ0/NoWMUZ5C0DW3dPm0wC1O76Y/SpKbuJzWPkx1EYy6r8p32Ly4D9vUzrKDbRGf48YHIF2kOkBmu21CLg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.303.0.tgz", + "integrity": "sha512-eO13PzdtRO9C+g3tyFOpIblX2SbDrIbg2bNtB8JOfjVi3E1b5VsSTXXU/cKV+lbZ9XMzMn3VzGSvpo6AjzfpxA==", "optional": true }, "@aws-sdk/shared-ini-file-loader": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.226.0.tgz", - "integrity": "sha512-661VQefsARxVyyV2FX9V61V+nNgImk7aN2hYlFKla6BCwZfMng+dEtD0xVGyg1PfRw0qvEv5LQyxMVgHcUSevA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.303.0.tgz", + "integrity": "sha512-yI84mnnh3pdQtIOo+oGWofaI0rvfhp3DOavB8KHIkQr+RcjF+fxsqbelRfVb25gx7yEWPNCMB8wM+HhklSEFJg==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/signature-v4": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.226.0.tgz", - "integrity": "sha512-/R5q5agdPd7HJB68XMzpxrNPk158EHUvkFkuRu5Qf3kkkHebEzWEBlWoVpUe6ss4rP9Tqcue6xPuaftEmhjpYw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.303.0.tgz", + "integrity": "sha512-muw5yclLOgXPHIxv60mhO6R0GVjKbf+M6E/cWvIEVGq8Ke+mLMYNFYNdKP/f/8JgTtW2xwQ7pIK3U8x284ZqPw==", "optional": true, "requires": { - "@aws-sdk/is-array-buffer": "3.201.0", - "@aws-sdk/types": "3.226.0", - "@aws-sdk/util-hex-encoding": "3.201.0", - "@aws-sdk/util-middleware": "3.226.0", - "@aws-sdk/util-uri-escape": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/is-array-buffer": "3.303.0", + "@aws-sdk/types": "3.303.0", + "@aws-sdk/util-hex-encoding": "3.295.0", + "@aws-sdk/util-middleware": "3.303.0", + "@aws-sdk/util-uri-escape": "3.303.0", + "@aws-sdk/util-utf8": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/smithy-client": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.234.0.tgz", - "integrity": "sha512-8AtR/k4vsFvjXeQbIzq/Wy7Nbk48Ou0wUEeVYPHWHPSU8QamFWORkOwmKtKMfHAyZvmqiAPeQqHFkq+UJhWyyQ==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.303.0.tgz", + "integrity": "sha512-WDTC9ODdpRAXo8+Mtr5hsPJeR3y3LxfZZFg5dplJgkaxV+MFdnsUCxZfAZMnxcGy5Q2qTzlLLNk9CpadS72v+g==", "optional": true, "requires": { - "@aws-sdk/middleware-stack": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/middleware-stack": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/token-providers": { - "version": "3.238.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.238.0.tgz", - "integrity": "sha512-vYUwmy0kTzA99mJCVvad+/5RDlWve/xxnppT8DJK3JIdAgskp+rULY+joVnq2NSl489UAioUnFGs57vUxi8Pog==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.303.0.tgz", + "integrity": "sha512-7G7VYbqyX0v6RTD/m7XmArZToMek4jYXR/TuuGHK6ifNJeMDwkU4BcoVDj37vvTPYp6qKU5IE+bE3XmPyVWnGQ==", "optional": true, "requires": { - "@aws-sdk/client-sso-oidc": "3.238.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/shared-ini-file-loader": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/client-sso-oidc": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/shared-ini-file-loader": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/types": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.226.0.tgz", - "integrity": "sha512-MmmNHrWeO4man7wpOwrAhXlevqtOV9ZLcH4RhnG5LmRce0RFOApx24HoKENfFCcOyCm5LQBlsXCqi0dZWDWU0A==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.303.0.tgz", + "integrity": "sha512-H+Cy8JDTsK87MID6MJbV9ad5xdS9YvaLZSeveC2Zs1WNu2Rp6X9j+mg3EqDSmBKUQVAFRy2b+CSKkH3nnBMedw==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/url-parser": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.226.0.tgz", - "integrity": "sha512-p5RLE0QWyP0OcTOLmFcLdVgUcUEzmEfmdrnOxyNzomcYb0p3vUagA5zfa1HVK2azsQJFBv28GfvMnba9bGhObg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.303.0.tgz", + "integrity": "sha512-PXMXGhr89s0MiPTf8Ft/v3sPzh2geSrFhTVSO/01blfBQqtuu0JMqORhLheOdi16AhQNVlYHDW2tWdx7/T+KsA==", "optional": true, "requires": { - "@aws-sdk/querystring-parser": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/querystring-parser": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-base64": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.208.0.tgz", - "integrity": "sha512-PQniZph5A6N7uuEOQi+1hnMz/FSOK/8kMFyFO+4DgA1dZ5pcKcn5wiFwHkcTb/BsgVqQa3Jx0VHNnvhlS8JyTg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.303.0.tgz", + "integrity": "sha512-oj+p/GHHPcZEKjiiOHU/CyNQeh8i+8dfMMzU+VGdoK5jHaVG8h2b+V7GPf7I4wDkG2ySCK5b5Jw5NUHwdTJ13Q==", "optional": true, "requires": { - "@aws-sdk/util-buffer-from": "3.208.0", - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-body-length-browser": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.188.0.tgz", - "integrity": "sha512-8VpnwFWXhnZ/iRSl9mTf+VKOX9wDE8QtN4bj9pBfxwf90H1X7E8T6NkiZD3k+HubYf2J94e7DbeHs7fuCPW5Qg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.303.0.tgz", + "integrity": "sha512-T643m0pKzgjAvPFy4W8zL+aszG3T22U8hb6stlMvT0z++Smv8QfIvkIkXjWyH2KlOt5GKliHwdOv8SAi0FSMJQ==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-body-length-node": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.208.0.tgz", - "integrity": "sha512-3zj50e5g7t/MQf53SsuuSf0hEELzMtD8RX8C76f12OSRo2Bca4FLLYHe0TZbxcfQHom8/hOaeZEyTyMogMglqg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.303.0.tgz", + "integrity": "sha512-/hS8z6e18Le60hJr2TUIFoUjUiAsnQsuDn6DxX74GXhMOHeSwZDJ9jHF39quYkNMmAE37GrVH4MI9vE0pN27qw==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-buffer-from": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.208.0.tgz", - "integrity": "sha512-7L0XUixNEFcLUGPeBF35enCvB9Xl+K6SQsmbrPk1P3mlV9mguWSDQqbOBwY1Ir0OVbD6H/ZOQU7hI/9RtRI0Zw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.303.0.tgz", + "integrity": "sha512-hUU+NW+SW6RNojtAKnnmz+tDShVKlEx2YsS4a5fSfrKRUes+zWz10cxVX0RQfysd3R6tdSHhbjsSj8eCIybheg==", "optional": true, "requires": { - "@aws-sdk/is-array-buffer": "3.201.0", - "tslib": "^2.3.1" + "@aws-sdk/is-array-buffer": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-config-provider": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.208.0.tgz", - "integrity": "sha512-DSRqwrERUsT34ug+anlMBIFooBEGwM8GejC7q00Y/9IPrQy50KnG5PW2NiTjuLKNi7pdEOlwTSEocJE15eDZIg==", + "version": "3.295.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.295.0.tgz", + "integrity": "sha512-/5Dl1aV2yI8YQjqwmg4RTnl/E9NmNsx7HIwBZt+dTcOrM0LMUwczQBFFcLyqCj/qv5y+VsvLoAAA/OiBT7hb3w==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-defaults-mode-browser": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.234.0.tgz", - "integrity": "sha512-IHMKXjTbOD8XMz5+2oCOsVP94BYb9YyjXdns0aAXr2NAo7k2+RCzXQ2DebJXppGda1F6opFutoKwyVSN0cmbMw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.303.0.tgz", + "integrity": "sha512-jtZgCKelFe4/SHDHQu9ydbYttxSfqSlQojA5qxTJxLvzryIB+/GTHQ+sYWyMyzaD489W9elt1/cSsXd4LtPK0A==", "optional": true, "requires": { - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-defaults-mode-node": { - "version": "3.234.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.234.0.tgz", - "integrity": "sha512-UGjQ+OjBYYhxFVtUY+jtr0ZZgzZh6OHtYwRhFt8IHewJXFCfZTyfsbX20szBj5y1S4HRIUJ7cwBLIytTqMbI5w==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.303.0.tgz", + "integrity": "sha512-c86iyot/u9bCVcy/rlWL+0kdR51c7C2d2yDXvO9iFCdMKAs28Hw1ijGczVmOcUQ61zKNFSGYx+VekHXN9IWYOg==", "optional": true, "requires": { - "@aws-sdk/config-resolver": "3.234.0", - "@aws-sdk/credential-provider-imds": "3.226.0", - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/property-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/config-resolver": "3.303.0", + "@aws-sdk/credential-provider-imds": "3.303.0", + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/property-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-endpoints": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.226.0.tgz", - "integrity": "sha512-iqOkac/zLmyPBUJd7SLN0PeZMkOmlGgD5PHmmekTClOkce2eUjK9SNX1PzL73aXPoPTyhg9QGLH8uEZEQ8YUzg==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.303.0.tgz", + "integrity": "sha512-dPg9+l3VY3nclWFiWAVNWek5lQwgdtY8oRYOgCeyntce9FlNrPQgCRTVr36D0iQ0aNCs0GWzfjgL+rIdCF66/w==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-hex-encoding": { - "version": "3.201.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.201.0.tgz", - "integrity": "sha512-7t1vR1pVxKx0motd3X9rI3m/xNp78p3sHtP5yo4NP4ARpxyJ0fokBomY8ScaH2D/B+U5o9ARxldJUdMqyBlJcA==", + "version": "3.295.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.295.0.tgz", + "integrity": "sha512-XJcoVo41kHzhe28PBm/rqt5mdCp8R6abwiW9ug1dA6FOoPUO8kBUxDv6xaOmA2hfRvd2ocFfBXaUCBqUowkGcQ==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-locate-window": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.208.0.tgz", - "integrity": "sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg==", + "version": "3.295.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.295.0.tgz", + "integrity": "sha512-d/s+zhUx5Kh4l/ecMP/TBjzp1GR/g89Q4nWH6+wH5WgdHsK+LG+vmsk6mVNuP/8wsCofYG4NBqp5Ulbztbm9QA==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-middleware": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.226.0.tgz", - "integrity": "sha512-B96CQnwX4gRvQdaQkdUtqvDPkrptV5+va6FVeJOocU/DbSYMAScLxtR3peMS8cnlOT6nL1Eoa42OI9AfZz1VwQ==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.303.0.tgz", + "integrity": "sha512-HAfBcbZw1+pY3dIEDM4jVpH1ViFcGH5s0q1dr+x4rcLGpMM3B4dH0HUgDPtycG8sw+nk+9jGgiEtgaCNOpJLGA==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-retry": { - "version": "3.229.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.229.0.tgz", - "integrity": "sha512-0zKTqi0P1inD0LzIMuXRIYYQ/8c1lWMg/cfiqUcIAF1TpatlpZuN7umU0ierpBFud7S+zDgg0oemh+Nj8xliJw==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.303.0.tgz", + "integrity": "sha512-RWwRNjoWMcpDouz69wPuFXWFVzwYtUkTbJfa46SjKl1IwqMHS4f9yjJfCwJIoLOW9M/o2JB7nD0Ij3gqqzajLw==", "optional": true, "requires": { - "@aws-sdk/service-error-classification": "3.229.0", - "tslib": "^2.3.1" + "@aws-sdk/service-error-classification": "3.303.0", + "tslib": "^2.5.0" } }, "@aws-sdk/util-uri-escape": { - "version": "3.201.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.201.0.tgz", - "integrity": "sha512-TeTWbGx4LU2c5rx0obHeDFeO9HvwYwQtMh1yniBz00pQb6Qt6YVOETVQikRZ+XRQwEyCg/dA375UplIpiy54mA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.303.0.tgz", + "integrity": "sha512-N3ULNuHCL3QzAlCTY+XRRkRQTYCTU8RRuzFCJX0pDpz9t2K+tLT7DbxqupWGNFGl5Xlulf1Is14J3BP/Dx91rA==", "optional": true, "requires": { - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-user-agent-browser": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.226.0.tgz", - "integrity": "sha512-PhBIu2h6sPJPcv2I7ELfFizdl5pNiL4LfxrasMCYXQkJvVnoXztHA1x+CQbXIdtZOIlpjC+6BjDcE0uhnpvfcA==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.303.0.tgz", + "integrity": "sha512-Kex3abpUrTX9z129jiI8sfjIUmQDwiWjhkvBkPmrwjFY/sZcnOcXj5nP2iwJ+k6CnA5ZK5PjZ6P62t+eJ5MTXw==", "optional": true, "requires": { - "@aws-sdk/types": "3.226.0", + "@aws-sdk/types": "3.303.0", "bowser": "^2.11.0", - "tslib": "^2.3.1" + "tslib": "^2.5.0" } }, "@aws-sdk/util-user-agent-node": { - "version": "3.226.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.226.0.tgz", - "integrity": "sha512-othPc5Dz/pkYkxH+nZPhc1Al0HndQT8zHD4e9h+EZ+8lkd8n+IsnLfTS/mSJWrfiC6UlNRVw55cItstmJyMe/A==", + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.303.0.tgz", + "integrity": "sha512-QYUg8F/Ho6AsVZaSSRMf/LWoEPDyOwgKZBw3AbKoH6RxAdAsdL1SXz5t4A6jHakP9TLVN2Yw2WRbHDe4LATASQ==", "optional": true, "requires": { - "@aws-sdk/node-config-provider": "3.226.0", - "@aws-sdk/types": "3.226.0", - "tslib": "^2.3.1" + "@aws-sdk/node-config-provider": "3.303.0", + "@aws-sdk/types": "3.303.0", + "tslib": "^2.5.0" } }, - "@aws-sdk/util-utf8-browser": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.188.0.tgz", - "integrity": "sha512-jt627x0+jE+Ydr9NwkFstg3cUvgWh56qdaqAMDsqgRlKD21md/6G226z/Qxl7lb1VEW2LlmCx43ai/37Qwcj2Q==", + "@aws-sdk/util-utf8": { + "version": "3.303.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.303.0.tgz", + "integrity": "sha512-tZXVuMOIONPOuOGBs/XRdzxv6jUvTM620dRFFIHZwlGiW8bo0x0LlonrzDAJZA4e9ZwmxJIj8Ji13WVRBGvZWg==", "optional": true, "requires": { - "tslib": "^2.3.1" + "@aws-sdk/util-buffer-from": "3.303.0", + "tslib": "^2.5.0" } }, - "@aws-sdk/util-utf8-node": { - "version": "3.208.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.208.0.tgz", - "integrity": "sha512-jKY87Acv0yWBdFxx6bveagy5FYjz+dtV8IPT7ay1E2WPWH1czoIdMAkc8tSInK31T6CRnHWkLZ1qYwCbgRfERQ==", + "@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", "optional": true, "requires": { - "@aws-sdk/util-buffer-from": "3.208.0", "tslib": "^2.3.1" } }, @@ -15556,23 +15596,23 @@ } }, "@mikro-orm/core": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-5.6.2.tgz", - "integrity": "sha512-FDKJBL1gjT9d36E9dyC5S9OxoeEQzy7lIj/SpijU8BDDC6gpn27HJ00DLqG+gLdepXBYdDdf3B/ELi+OGxZKCw==", + "version": "5.6.16", + "resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-5.6.16.tgz", + "integrity": "sha512-JTrVS4Rb5uVKbf/d26Ni/YgJjMyoHapPEUklr4H+4c+6xlRXbfpPDN6o4ESWNXTc4ubwRGLMI1haMXg0bh6uVQ==", "requires": { "acorn-loose": "8.3.0", "acorn-walk": "8.2.0", "dotenv": "16.0.3", - "fs-extra": "11.1.0", + "fs-extra": "11.1.1", "globby": "11.1.0", - "mikro-orm": "~5.6.2", + "mikro-orm": "~5.6.16", "reflect-metadata": "0.1.13" } }, "@mikro-orm/mongodb": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@mikro-orm/mongodb/-/mongodb-5.6.2.tgz", - "integrity": "sha512-z3dE0Bi2EgqCIZUnIzQyC1dlpBv3AmoUwvmmhvVmoz44avY0ZkZiXA/RFPocwvL9DZzKLI5zjMTj7dX9KPSyXw==", + "version": "5.6.16", + "resolved": "https://registry.npmjs.org/@mikro-orm/mongodb/-/mongodb-5.6.16.tgz", + "integrity": "sha512-MH7B4+OkQedLkv+ecTDRD41La8255LfpCuuwHNOnE8+XjF0tCXiD1XO4Cpcau98ubLR959HRs88OtulQwzgCQw==", "requires": { "bson": "^4.7.0", "mongodb": "4.13.0" @@ -16371,50 +16411,116 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "@reldens/items-system": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@reldens/items-system/-/items-system-0.16.3.tgz", - "integrity": "sha512-8n/VcvdZjInPWRIsEqkGbDinrcrnFonVTzM87O/S9VfGfCWMzsuer76cUL5dCiOM6PNv5Fb5UVzWvFixv0S1CQ==", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@reldens/items-system/-/items-system-0.18.1.tgz", + "integrity": "sha512-gTKCb/Y2THC+Gd0V1IO8HiYHAk5oOvJMcIXCe/NX8TVrFs1Rr2mkWvMa30fBB37cei39zXGaiBdza1sSam3zWQ==", "requires": { - "@reldens/modifiers": "^0.17.1", - "@reldens/storage": "^0.12.2", - "@reldens/utils": "^0.17.7" + "@reldens/modifiers": "^0.19.0", + "@reldens/storage": "^0.13.0", + "@reldens/utils": "^0.19.0" + }, + "dependencies": { + "@reldens/storage": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.13.0.tgz", + "integrity": "sha512-b9iuTk+szM7I9RGEcXuhK295EBhlqQ1K8LjUCvdJwsEHrxuz14EYCMZppykg8FEh1LYO1LCWQrOuywOPf7oEtg==", + "requires": { + "@mikro-orm/core": "^5.6.7", + "@mikro-orm/mongodb": "^5.6.7", + "@reldens/utils": "^0.18.0", + "knex": "^2.4.1", + "mysql": "^2.18.1", + "objection": "^3.0.1" + }, + "dependencies": { + "@reldens/utils": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.18.1.tgz", + "integrity": "sha512-ysWT+grnzdkrISMFsXvMBOMhknoIp9EvKkDMuUSB0zl+KzeEULqGcMz1B0MBXPbULVNz70DBExqVNKxFJUXWvg==", + "requires": { + "await-event-emitter": "^2.0.2" + } + } + } + }, + "@reldens/utils": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.19.0.tgz", + "integrity": "sha512-GhRQaOotANHyAmDu6so1OJQwSyzmtFymzQXPQt8yFweRqCJudOtNxq2pYd9C5fN7NGC5W6w+lXUbhjSN2erpUA==", + "requires": { + "await-event-emitter": "^2.0.2" + } + } } }, "@reldens/modifiers": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@reldens/modifiers/-/modifiers-0.17.1.tgz", - "integrity": "sha512-zPEVGv5Q2C9RKJZcMC1afXMVadKJ97b47wELxXwlaDsD4AEuc1nQiJrHUZcC7maZtiOtfxHrbasO8u2/K3GMnA==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@reldens/modifiers/-/modifiers-0.19.1.tgz", + "integrity": "sha512-WBN2WCxszwa00tVgGWqwcYeEuF7NrN1FkETWT2Q6lFSwbkIpVMxuwcz9gXsSJHG0fvPZHBfIlgYsHxc45hQO6Q==", "requires": { - "@reldens/utils": "^0.17.7" + "@reldens/utils": "^0.20.0" } }, "@reldens/skills": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@reldens/skills/-/skills-0.16.6.tgz", - "integrity": "sha512-UfqmZqRAodaWTq/Qzm6tuxHi81jeYlAfYElbtn/XDl/qCHO75O/enXDMy2hUndl3G+A1w8DLmm2ziFERm3zDrg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@reldens/skills/-/skills-0.18.0.tgz", + "integrity": "sha512-pHPeF+1W7GJyUp0ZJ4lshBNbV+ec6uyW24KMequIhfmvQnRmBbhplV1VjRkqFBDiKLKHjbO4tSBzC0nj0UWoeg==", "requires": { - "@reldens/modifiers": "^0.17.1", - "@reldens/storage": "^0.12.2", - "@reldens/utils": "^0.17.8" + "@reldens/modifiers": "^0.19.0", + "@reldens/storage": "^0.13.0", + "@reldens/utils": "^0.19.0" + }, + "dependencies": { + "@reldens/storage": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.13.0.tgz", + "integrity": "sha512-b9iuTk+szM7I9RGEcXuhK295EBhlqQ1K8LjUCvdJwsEHrxuz14EYCMZppykg8FEh1LYO1LCWQrOuywOPf7oEtg==", + "requires": { + "@mikro-orm/core": "^5.6.7", + "@mikro-orm/mongodb": "^5.6.7", + "@reldens/utils": "^0.18.0", + "knex": "^2.4.1", + "mysql": "^2.18.1", + "objection": "^3.0.1" + }, + "dependencies": { + "@reldens/utils": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.18.1.tgz", + "integrity": "sha512-ysWT+grnzdkrISMFsXvMBOMhknoIp9EvKkDMuUSB0zl+KzeEULqGcMz1B0MBXPbULVNz70DBExqVNKxFJUXWvg==", + "requires": { + "await-event-emitter": "^2.0.2" + } + } + } + }, + "@reldens/utils": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.19.0.tgz", + "integrity": "sha512-GhRQaOotANHyAmDu6so1OJQwSyzmtFymzQXPQt8yFweRqCJudOtNxq2pYd9C5fN7NGC5W6w+lXUbhjSN2erpUA==", + "requires": { + "await-event-emitter": "^2.0.2" + } + } } }, "@reldens/storage": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.12.2.tgz", - "integrity": "sha512-lpgCCnaEnmgud/4OS7vF+LRvDtcH8RgD6cbqZULNHFN6mGLS/fwzCEBAaQqIIC9xxHarwbNxTSgNFpM4S+mfMA==", - "requires": { - "@mikro-orm/core": "^5.6.1", - "@mikro-orm/mongodb": "^5.6.1", - "@reldens/utils": "^0.17.7", - "knex": "^2.3.0", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.15.0.tgz", + "integrity": "sha512-xCzFREU0IxmSdnSsYjOp8ym2X5G/MfTFw0NWtfoYWLHJ17RbN9x3KpnCBMkllJzkb19tGUO3pqX1L9bocgSDgg==", + "requires": { + "@mikro-orm/core": "^5.6.16", + "@mikro-orm/mongodb": "^5.6.16", + "@reldens/utils": "^0.20.0", + "knex": "^2.4.2", "mysql": "^2.18.1", "objection": "^3.0.1" } }, "@reldens/utils": { - "version": "0.17.8", - "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.17.8.tgz", - "integrity": "sha512-f6ssPTlFaji4tTWERxx+1Cz5zu13o23pmK75Xk0jxauk2kcq9JclwHcTPl8C3ojzQCHVhST1gBTeUOAVwLZ7OQ==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.20.0.tgz", + "integrity": "sha512-WzipJU4yoH68mVWh1+oTEI7ZVoxRTLli81ZI74v1KK16zihFG3YoJ5TIIQg+7BJMTjEkUXj3+PoEGRTON8KjLQ==", "requires": { "await-event-emitter": "^2.0.2" } @@ -17239,9 +17345,9 @@ } }, "bson": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.0.tgz", - "integrity": "sha512-VrlEE4vuiO1WTpfof4VmaVolCVYkYTgB9iWgYNOrVlnifpME/06fhFRmONgBhClD5pFC1t9ZWqFUQEQAzY43bA==", + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", "requires": { "buffer": "^5.6.0" }, @@ -18139,18 +18245,18 @@ "optional": true }, "fast-xml-parser": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz", - "integrity": "sha512-4aUg3aNRR/WjQAcpceODG1C3x3lFANXRo8+1biqfieHmg9pyMt7qB4lQV/Ta6sJCTbA5vfD8fnA8S54JATiFUA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.1.2.tgz", + "integrity": "sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==", "optional": true, "requires": { "strnum": "^1.0.5" } }, "fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "requires": { "reusify": "^1.0.4" } @@ -18313,9 +18419,9 @@ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -19092,9 +19198,9 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "knex": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/knex/-/knex-2.3.0.tgz", - "integrity": "sha512-WMizPaq9wRMkfnwKXKXgBZeZFOSHGdtoSz5SaLAVNs3WRDfawt9O89T4XyH52PETxjV8/kRk0Yf+8WBEP/zbYw==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/knex/-/knex-2.4.2.tgz", + "integrity": "sha512-tMI1M7a+xwHhPxjbl/H9K1kHX+VncEYcvCx5K00M16bWvpYPKAZd6QrCu68PtHAdIZNQPWZn0GVhqVBEthGWCg==", "requires": { "colorette": "2.0.19", "commander": "^9.1.0", @@ -19353,9 +19459,9 @@ } }, "mikro-orm": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-5.6.2.tgz", - "integrity": "sha512-LzKIAtA/Zj8qO1yKNRJLYI+Vywm1nBQqb89vPM7abZBnqqarGB89CWTlAt6DfaG9bNk7v+FK0atO5dqMEfLT1Q==" + "version": "5.6.16", + "resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-5.6.16.tgz", + "integrity": "sha512-HgG079qA5hWgGWlq9u3BjgE3ynGnDFsGRtvFhgo6W3Itkz46SsQ4oeQxRcAetd8mj/qM4SOLuy0k71pI6h0PkQ==" }, "mime": { "version": "3.0.0", @@ -21320,9 +21426,9 @@ } }, "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "type-fest": { "version": "0.20.2", diff --git a/package.json b/package.json index 59ec3d2de..19333d8f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "reldens", - "version": "4.0.0-beta.25", + "version": "4.0.0-beta.26", "description": "Reldens - MMORPG Platform", "author": "Damian A. Pastorini", "license": "MIT", @@ -37,9 +37,9 @@ "@adminjs/design-system": "2.1.2", "@adminjs/express": "4.1.0", "@adminjs/upload": "2.0.2", - "@colyseus/monitor": "^0.14.22", - "@colyseus/schema": "^2.0.4", - "@colyseus/ws-transport": "^0.14.21", + "@colyseus/monitor": "0.14.22", + "@colyseus/schema": "2.0.4", + "@colyseus/ws-transport": "0.14.21", "@parcel/config-default": "2.8.0", "@parcel/bundler-default": "2.8.0", "@parcel/transformer-inline-string": "2.8.0", @@ -78,32 +78,32 @@ "@parcel/resolver-default": "2.8.0", "@parcel/reporter-dev-server": "2.8.0", "@parcel/core": "2.8.0", - "@reldens/items-system": "^0.16.3", - "@reldens/modifiers": "^0.17.1", - "@reldens/skills": "^0.16.6", - "@reldens/storage": "^0.12.2", - "@reldens/utils": "^0.17.8", + "@reldens/items-system": "^0.18.1", + "@reldens/modifiers": "^0.19.1", + "@reldens/skills": "^0.18.0", + "@reldens/storage": "^0.15.0", + "@reldens/utils": "^0.20.0", "adminjs": "5.7.3", - "bcrypt": "^5.1.0", + "bcrypt": "5.1.0", "colyseus": "0.14.24", "colyseus.js": "0.14.13", - "core-js": "^3.26.1", + "core-js": "3.26.1", "cors": "2.8.5", - "dotenv": "^16.0.3", - "express": "^4.18.2", - "express-basic-auth": "^1.2.1", - "express-formidable": "^1.2.0", - "express-session": "^1.17.3", + "dotenv": "16.0.3", + "express": "4.18.2", + "express-basic-auth": "1.2.1", + "express-formidable": "1.2.0", + "express-session": "1.17.3", "firebase": "9.9.3", "firebaseui": "6.0.2", - "mime-types": "^2.1.35", - "mustache": "^4.2.0", - "nodemailer": "^6.8.0", - "p2": "^0.7.1", - "pathfinding": "^0.4.18", + "mime-types": "2.1.35", + "mustache": "4.2.0", + "nodemailer": "6.8.0", + "p2": "0.7.1", + "pathfinding": "0.4.18", "phaser": "3.55.2", - "react": "^16.13.1", - "regenerator-runtime": "^0.13.11", - "tslib": "^2.4.1" + "react": "16.14.0", + "regenerator-runtime": "0.13.11", + "tslib": "2.5.0" } } diff --git a/theme/default/assets/custom/sprites/branch-sprite.png b/theme/default/assets/custom/sprites/branch-sprite.png new file mode 100644 index 000000000..f2ea395a6 Binary files /dev/null and b/theme/default/assets/custom/sprites/branch-sprite.png differ diff --git a/theme/default/assets/favicon/android-chrome-192x192.png b/theme/default/assets/favicon/android-chrome-192x192.png index 4293e4cbf..2a39620f1 100644 Binary files a/theme/default/assets/favicon/android-chrome-192x192.png and b/theme/default/assets/favicon/android-chrome-192x192.png differ diff --git a/theme/default/assets/favicon/android-chrome-512x512.png b/theme/default/assets/favicon/android-chrome-512x512.png index 6b5d25d9a..5ac07321a 100644 Binary files a/theme/default/assets/favicon/android-chrome-512x512.png and b/theme/default/assets/favicon/android-chrome-512x512.png differ diff --git a/theme/default/assets/favicon/apple-touch-icon.png b/theme/default/assets/favicon/apple-touch-icon.png index 14aea5c6f..3e15f3aeb 100644 Binary files a/theme/default/assets/favicon/apple-touch-icon.png and b/theme/default/assets/favicon/apple-touch-icon.png differ diff --git a/theme/default/assets/favicon/favicon-16x16.png b/theme/default/assets/favicon/favicon-16x16.png index d61a69dc7..2201712cf 100644 Binary files a/theme/default/assets/favicon/favicon-16x16.png and b/theme/default/assets/favicon/favicon-16x16.png differ diff --git a/theme/default/assets/favicon/favicon-32x32.png b/theme/default/assets/favicon/favicon-32x32.png index bca5c0e9d..eaca46a74 100644 Binary files a/theme/default/assets/favicon/favicon-32x32.png and b/theme/default/assets/favicon/favicon-32x32.png differ diff --git a/theme/default/assets/features/inventory/assets/equipment.png b/theme/default/assets/features/inventory/assets/equipment.png index 58045ad34..a79c066b6 100644 Binary files a/theme/default/assets/features/inventory/assets/equipment.png and b/theme/default/assets/features/inventory/assets/equipment.png differ diff --git a/theme/default/assets/features/inventory/assets/equipped.png b/theme/default/assets/features/inventory/assets/equipped.png index 67c50201f..b0b439a39 100644 Binary files a/theme/default/assets/features/inventory/assets/equipped.png and b/theme/default/assets/features/inventory/assets/equipped.png differ diff --git a/theme/default/assets/features/inventory/assets/inventory.png b/theme/default/assets/features/inventory/assets/inventory.png index bd29b8a14..479aaf7bf 100644 Binary files a/theme/default/assets/features/inventory/assets/inventory.png and b/theme/default/assets/features/inventory/assets/inventory.png differ diff --git a/theme/default/assets/features/inventory/assets/team.png b/theme/default/assets/features/inventory/assets/team.png new file mode 100644 index 000000000..d3c1ed0a0 Binary files /dev/null and b/theme/default/assets/features/inventory/assets/team.png differ diff --git a/theme/default/assets/features/inventory/assets/trash.png b/theme/default/assets/features/inventory/assets/trash.png index f04848558..add962db4 100644 Binary files a/theme/default/assets/features/inventory/assets/trash.png and b/theme/default/assets/features/inventory/assets/trash.png differ diff --git a/theme/default/assets/features/inventory/assets/unequipped.png b/theme/default/assets/features/inventory/assets/unequipped.png index 3428d3d5a..e28b66734 100644 Binary files a/theme/default/assets/features/inventory/assets/unequipped.png and b/theme/default/assets/features/inventory/assets/unequipped.png differ diff --git a/theme/default/assets/features/inventory/assets/use.png b/theme/default/assets/features/inventory/assets/use.png index 7ad8ce67f..910b68309 100644 Binary files a/theme/default/assets/features/inventory/assets/use.png and b/theme/default/assets/features/inventory/assets/use.png differ diff --git a/theme/default/assets/features/teams/assets/clan.png b/theme/default/assets/features/teams/assets/clan.png new file mode 100644 index 000000000..ed56004c2 Binary files /dev/null and b/theme/default/assets/features/teams/assets/clan.png differ diff --git a/theme/default/assets/features/teams/assets/team.png b/theme/default/assets/features/teams/assets/team.png new file mode 100644 index 000000000..d3c1ed0a0 Binary files /dev/null and b/theme/default/assets/features/teams/assets/team.png differ diff --git a/theme/default/assets/features/teams/templates/clan-accept.html b/theme/default/assets/features/teams/templates/clan-accept.html new file mode 100644 index 000000000..a9998fbb5 --- /dev/null +++ b/theme/default/assets/features/teams/templates/clan-accept.html @@ -0,0 +1,4 @@ +
+ {{acceptTeamFromPlayerLabel}} + +
diff --git a/theme/default/assets/features/teams/templates/clan-container.html b/theme/default/assets/features/teams/templates/clan-container.html new file mode 100644 index 000000000..3213bb43e --- /dev/null +++ b/theme/default/assets/features/teams/templates/clan-container.html @@ -0,0 +1,19 @@ +
+
+
{{clanPlayersTitle}}
+
+ {{&clanPlayers}} +
+
+
+
{{clanMembersTitle}}
+
+ {{&clanMembers}} +
+
+
+
+ +
+
+
diff --git a/theme/default/assets/features/teams/templates/clan-create.html b/theme/default/assets/features/teams/templates/clan-create.html new file mode 100644 index 000000000..69935b9c1 --- /dev/null +++ b/theme/default/assets/features/teams/templates/clan-create.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/theme/default/assets/features/teams/templates/clan-invite.html b/theme/default/assets/features/teams/templates/clan-invite.html new file mode 100644 index 000000000..8743e9fb2 --- /dev/null +++ b/theme/default/assets/features/teams/templates/clan-invite.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/theme/default/assets/features/teams/templates/clan-member-data.html b/theme/default/assets/features/teams/templates/clan-member-data.html new file mode 100644 index 000000000..a7c9608f9 --- /dev/null +++ b/theme/default/assets/features/teams/templates/clan-member-data.html @@ -0,0 +1,6 @@ +
+
+ {{playerId}} - {{playerName}} +
+ {{&clanRemove}} +
diff --git a/theme/default/assets/features/teams/templates/clan-player-data.html b/theme/default/assets/features/teams/templates/clan-player-data.html new file mode 100644 index 000000000..f889784f9 --- /dev/null +++ b/theme/default/assets/features/teams/templates/clan-player-data.html @@ -0,0 +1,8 @@ +
+
+ {{playerId}} - {{playerName}} +
+
+ {{&playerProperties}} +
+
diff --git a/theme/default/assets/features/teams/templates/clan-remove.html b/theme/default/assets/features/teams/templates/clan-remove.html new file mode 100644 index 000000000..a95c48074 --- /dev/null +++ b/theme/default/assets/features/teams/templates/clan-remove.html @@ -0,0 +1,3 @@ +
+ remove +
diff --git a/theme/default/assets/features/teams/templates/shared-property.html b/theme/default/assets/features/teams/templates/shared-property.html new file mode 100644 index 000000000..dca0ba533 --- /dev/null +++ b/theme/default/assets/features/teams/templates/shared-property.html @@ -0,0 +1,11 @@ +
+
+ {{label}} +
+
+ {{value}} +
+
+ {{max}} +
+
diff --git a/theme/default/assets/features/teams/templates/team-accept.html b/theme/default/assets/features/teams/templates/team-accept.html new file mode 100644 index 000000000..a3163807b --- /dev/null +++ b/theme/default/assets/features/teams/templates/team-accept.html @@ -0,0 +1,4 @@ +
+ {{acceptTeamFromPlayerLabel}} + +
diff --git a/theme/default/assets/features/teams/templates/team-container.html b/theme/default/assets/features/teams/templates/team-container.html new file mode 100644 index 000000000..bc9ad1fa0 --- /dev/null +++ b/theme/default/assets/features/teams/templates/team-container.html @@ -0,0 +1,10 @@ +
+
+ {{&teamMembers}} +
+
+
+ +
+
+
diff --git a/theme/default/assets/features/teams/templates/team-invite.html b/theme/default/assets/features/teams/templates/team-invite.html new file mode 100644 index 000000000..af582e411 --- /dev/null +++ b/theme/default/assets/features/teams/templates/team-invite.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/theme/default/assets/features/teams/templates/team-player-data.html b/theme/default/assets/features/teams/templates/team-player-data.html new file mode 100644 index 000000000..304f77003 --- /dev/null +++ b/theme/default/assets/features/teams/templates/team-player-data.html @@ -0,0 +1,9 @@ +
+
+ {{playerName}} +
+ {{&playerRemove}} +
+ {{&playerProperties}} +
+
diff --git a/theme/default/assets/features/teams/templates/team-remove.html b/theme/default/assets/features/teams/templates/team-remove.html new file mode 100644 index 000000000..63ee3fd2d --- /dev/null +++ b/theme/default/assets/features/teams/templates/team-remove.html @@ -0,0 +1,3 @@ +
+ remove +
diff --git a/theme/default/assets/features/teams/templates/ui-clan.html b/theme/default/assets/features/teams/templates/ui-clan.html new file mode 100644 index 000000000..a66946a96 --- /dev/null +++ b/theme/default/assets/features/teams/templates/ui-clan.html @@ -0,0 +1,6 @@ +
+ close +
{{title}}
+
{{content}}
+
+open diff --git a/theme/default/assets/features/teams/templates/ui-teams.html b/theme/default/assets/features/teams/templates/ui-teams.html new file mode 100644 index 000000000..b6f70229e --- /dev/null +++ b/theme/default/assets/features/teams/templates/ui-teams.html @@ -0,0 +1,6 @@ +
+ close +
{{title}}
+
{{content}}
+
+open diff --git a/theme/default/assets/html/ui-loading.html b/theme/default/assets/html/ui-loading.html new file mode 100644 index 000000000..672abeb84 --- /dev/null +++ b/theme/default/assets/html/ui-loading.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/theme/default/assets/icons/book.png b/theme/default/assets/icons/book.png index 3fd0ea9f4..39b820ba2 100644 Binary files a/theme/default/assets/icons/book.png and b/theme/default/assets/icons/book.png differ diff --git a/theme/default/assets/icons/instructions.png b/theme/default/assets/icons/instructions.png index 1f7d869a3..963ad8620 100644 Binary files a/theme/default/assets/icons/instructions.png and b/theme/default/assets/icons/instructions.png differ diff --git a/theme/default/assets/icons/minimap.png b/theme/default/assets/icons/minimap.png index 734c40a8e..19e2aebf6 100644 Binary files a/theme/default/assets/icons/minimap.png and b/theme/default/assets/icons/minimap.png differ diff --git a/theme/default/css/base.scss b/theme/default/css/base.scss index e76031ecd..367b6e2a7 100644 --- a/theme/default/css/base.scss +++ b/theme/default/css/base.scss @@ -294,7 +294,7 @@ form { .box-target { display: none; position: relative; - top: 150px; + top: 170px; left: 10px; #target-container { @@ -407,6 +407,10 @@ form { cursor: pointer; } +.box-open { + cursor: pointer; +} + .box-content { min-height: 40px; max-height: 120px; diff --git a/theme/default/css/chat.scss b/theme/default/css/chat.scss index c04b7725c..dafc4af87 100644 --- a/theme/default/css/chat.scss +++ b/theme/default/css/chat.scss @@ -15,6 +15,10 @@ background: #000000; opacity: 0.8; color: $cWhite; + /* help for chrome issue with scale on phaser dom elements + will-change: transform; + transform: scale(1.011); + */ } #chat-messages { @@ -27,6 +31,7 @@ #chat-input { width: 80%; + outline: none; } #chat-send { diff --git a/theme/default/css/styles.scss b/theme/default/css/styles.scss index 122eaa192..350b2e2a7 100644 --- a/theme/default/css/styles.scss +++ b/theme/default/css/styles.scss @@ -22,3 +22,5 @@ $cBlack: #000; @import "firebase"; @import "chat"; @import "items-system"; +@import "teams"; +@import "terms-and-conditions"; diff --git a/theme/default/css/teams.scss b/theme/default/css/teams.scss new file mode 100644 index 000000000..e1ac8d086 --- /dev/null +++ b/theme/default/css/teams.scss @@ -0,0 +1,153 @@ +/** + * + * Reldens - Styles - Teams + * + */ + + +.teams-dialog-box { + min-width: 160px; + top: 200px; + right: 360px; +} + +.clan-dialog-box { + float: left; + position: relative; + min-width: 160px; + min-height: 100px; + top: 250px; + right: 360px; + /* help for chrome issue with scale on phaser dom elements + will-change: transform; + transform: scale(1.009); + */ + + .clan-row { + display: block; + float: left; + width: 100%; + margin-top: 10px; + } + + .clan-name-input { + outline: none; + } + + .default-loading-container img{ + max-width: 48px; + } + +} + +@media (max-height: 400px) { + + .teams-dialog-box { + top: 50px; + } + + .clan-dialog-box { + top: 80px; + } + +} + +.clan-open { + position: relative; + top: 230px; + right: 60px; + background: rgba(0, 0, 0, 0.5); + padding: 10px; + max-width: 40px; +} + +.teams-open { + position: relative; + top: 300px; + right: 60px; + background: rgba(0, 0, 0, 0.5); + padding: 10px; + max-width: 40px; +} + +.team-player, +.property-box, +.properties-list-container { + width: 100%; + float: left; + display: block; + position: relative; + padding: 0; + margin: 0 0 5px; +} + +.player-name { + float: left; + display: block; + width: 90%; + text-align: left; + cursor: pointer; +} + +.properties-list-container { + cursor: pointer; +} + +.team-remove-container { + float: left; + display: block; + width: 10%; + + .team-remove-button { + position: absolute; + top: 0; + right: 0; + max-width: 24px; + cursor: pointer; + } + +} + +.property-box { + float: left; + padding: 0; + margin: 0; + + div { + float: left; + } + + .label { + margin-right: 10px; + } + + .value { + margin-right: 6px; + } + +} + +.team-disband-action { + float: right; +} + +.clan-member { + min-height: 24px; + vertical-align: middle; + line-height: 24px; + + .member-name { + float: left; + } + + .clan-remove-container { + float: right; + cursor: pointer; + + img { + max-width: 24px; + position: relative; + top: 0px; + } + } +} diff --git a/theme/default/css/terms-and-conditions.scss b/theme/default/css/terms-and-conditions.scss new file mode 100644 index 000000000..50d3f6364 --- /dev/null +++ b/theme/default/css/terms-and-conditions.scss @@ -0,0 +1,60 @@ +/** + * + * Reldens - Styles - Terms and Conditions + * + */ + +#terms-and-conditions { + z-index: 2000; + position: absolute; + top: 20%; + overflow: visible; + width: 46%; + margin: 0; + left: 25%; + right: auto; + background-color: rgba(0, 0, 0, 0.7); + color: $cWhite; + cursor: default; + + .scrollable { + max-height: 280px; + padding: 2% 4%; + } + + .terms-and-conditions-text { + display: block; + float: left; + width: 100%; + overflow-x: hidden; + overflow-y: auto; + margin-bottom: 15px; + + h3 { + text-align: center; + } + + .terms-body { + overflow-y: auto; + width: 92%; + padding: 2% 4%; + } + } +} + +.terms-and-conditions-link-container.hidden { + display: none; +} + +.terms-and-conditions-link { + float: right; + cursor: pointer; + font-size: 12px; + text-decoration: underline; +} + +.terms-box { + display: block; + padding: 2%; + margin: 2%; +} diff --git a/theme/default/favicon.ico b/theme/default/favicon.ico index 094cf7a57..0c77ba35d 100644 Binary files a/theme/default/favicon.ico and b/theme/default/favicon.ico differ diff --git a/theme/default/index.html b/theme/default/index.html index a04558364..164fee094 100644 --- a/theme/default/index.html +++ b/theme/default/index.html @@ -59,7 +59,7 @@

Login

Firebase Login

- +
@@ -90,6 +90,9 @@

Create Account

+
@@ -97,7 +100,7 @@

Create Account

Loading...
@@ -122,6 +125,19 @@

Forgot password

+