diff --git a/README.md b/README.md index 9abdaf4f8..0e2529272 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ While the current stage of the platform is tailored for developers, ongoing impr --- +## [Check our website for the latest news!](https://www.reldens.com/ "Check our website for the latest news") + +--- + ## [Features Overview](https://www.reldens.com/features) [First to mention, if the feature you need is not available, you can request a feature here: https://www.reldens.com/features-request](https://www.reldens.com/features-request) @@ -43,11 +47,9 @@ As for the latest version released the platform will provide you with the follow --- -## [Check our website for the latest news!](https://www.reldens.com/ "Check our website for the latest news") - ---- +## [Installation](https://www.reldens.com/documentation/installation "Installation") -## [Installation Guide](https://www.reldens.com/installation "Installation Guide") +Please follow the Installation Guide: https://www.reldens.com/documentation/installation. --- diff --git a/lib/actions/client/player-selector.js b/lib/actions/client/player-selector.js index bd8e9208f..6a19f48b1 100644 --- a/lib/actions/client/player-selector.js +++ b/lib/actions/client/player-selector.js @@ -92,7 +92,9 @@ class PlayerSelector let avatarKey = select.options[select.selectedIndex].dataset.key; avatar.classList.add('class-path-select-avatar'); avatar.style.backgroundImage = `url('/assets/custom/sprites/${avatarKey}${GameConst.FILES.EXTENSIONS.PNG}')`; - avatar.style.width = playersConfig.size.width+'px'; + let widthInPx = playersConfig.size.width+'px'; + avatar.style.backgroundPositionX = '-'+widthInPx; + avatar.style.width = widthInPx; avatar.style.height = playersConfig.size.height+'px'; select.addEventListener('change', () => { let avatarKey = select.options[select.selectedIndex].dataset.key; diff --git a/lib/actions/client/preloader-handler.js b/lib/actions/client/preloader-handler.js index a6198d375..d26e94918 100644 --- a/lib/actions/client/preloader-handler.js +++ b/lib/actions/client/preloader-handler.js @@ -2,48 +2,6 @@ * * Reldens - PreloaderHandler. * - * Main functionalities: - * The PreloaderHandler class is responsible for handling the preloading and creation of animations and assets used in - * the game. It loads HTML templates, spritesheets, and creates animations based on the configuration data provided by - * the game manager. It also handles the preparation and creation of animations with multiple directions and the - * creation of avatars animations. - * - * Methods: - * - constructor(props): initializes the class with the game manager and events manager objects, and sets the - * properties. - * - setProperties(props): sets the properties of the class, such as the game DOM, initial game data, and animations - * configuration. - * - loadContents(uiScene): loads HTML templates and preloads animations based on the configuration data. - * - preloadClassPaths(uiScene): preloads spritesheets for class paths based on the initial game data. - * - createAnimations(preloadScene): creates animations based on the configuration data. - * - createAvatarsAnimations(preloadScene): creates animations for avatars based on the initial game data. - * - loopAnimationsAnd(animations, command, uiScene): loops through the animations and executes the specified command - * (preload or create) for each animation. - * - preloadAnimation(data, uiScene): preloads animations based on the configuration data, including animations with - * multiple directions. - * - preloadAnimationsInDirections(data, uiScene): preloads animations in different directions based on the - * configuration data. - * - preloadSpriteInDirection(uiScene, data, direction): preloads a sprite in a specific direction based on the - * configuration data. - * - createAnimation(data, uiScene): creates animations based on the configuration data, including animations with - * multiple directions. - * - createWithMultipleDirections(uiScene, data, animDir): creates animations with multiple directions based on the - * configuration data. - * - createWithDirection(data, uiScene, direction = false): creates animations in a specific direction based on the - * configuration data. - * - prepareAnimationData(data, uiScene, direction = false): prepares the animation data based on the configuration - * data and direction. - * - getAnimationKey(data, direction = false): gets the animation key based on the configuration data and direction. - * - * Fields: - * - gameManager: the game manager object. - * - events: the events manager object. - * - gameDom: the game DOM object. - * - initialGameData: the initial game data object. - * - levelsAnimConfig: the levels animations configuration object. - * - skillsAnimConfig: the skills animations configuration object. - * - assetsCustomActionsSpritesPath: the path to the custom actions sprites assets. - * */ const { Logger, sc } = require('@reldens/utils'); @@ -79,7 +37,12 @@ class PreloaderHandler 'assetsCustomActionsSpritesPath', 'assets/custom/actions/sprites/' ); - this.gameManager.loadedAssets = {}; + if(!this.gameManager.loadedAssets){ + this.gameManager.loadedAssets = {}; + } + if(!this.gameManager.createdAnimations){ + this.gameManager.createdAnimations = {}; + } } loadContents(uiScene) @@ -127,34 +90,38 @@ class PreloaderHandler { let classesData = sc.get(this.initialGameData, 'classesData', false); if(!classesData){ + Logger.debug('Classes data not found. Fallback to player avatar.'); return false; } if(!this.gameManager.mappedAvatars){ this.gameManager.mappedAvatars = {}; } + Logger.debug({availableClassesData: classesData}); for(let i of Object.keys(classesData)){ let avatarKey = classesData[i].key; if(!this.gameManager.loadedAssets[avatarKey]){ + avatarKey = GameConst.IMAGE_PLAYER; Logger.info('Avatar for class path "'+avatarKey+'" not found in assets. Fallback to player avatar.'); - this.gameManager.mappedAvatars[avatarKey] = GameConst.IMAGE_PLAYER; - continue; } this.gameManager.mappedAvatars[avatarKey] = avatarKey; - return preloadScene.createPlayerAnimations(avatarKey); + preloadScene.createPlayerAnimations(avatarKey); } + return this.gameManager.mappedAvatars; } loopAnimationsAnd(animations, command, uiScene) { if(!animations){ + Logger.warning('Animations not found.', animations); return false; } for(let i of Object.keys(animations)){ let data = animations[i]; if(!data.animationData.enabled){ + Logger.debug('Animation "'+i+'" not enabled, skipping.', data); continue; } - // preloadAnimation or createAnimation + Logger.debug({[command+'Animation']: data}); this[command+'Animation'](data, uiScene); } } @@ -242,19 +209,24 @@ class PreloaderHandler } } - createWithDirection(data, uiScene, direction = false) + createWithDirection(data, uiScene, direction = '') { let animationCreateData = this.prepareAnimationData(data, uiScene, direction); - let animation = uiScene.anims.create(animationCreateData); + if(this.gameManager.createdAnimations[animationCreateData.key]){ + return this.gameManager.createdAnimations[animationCreateData.key]; + } + let newAnimation = uiScene.anims.create(animationCreateData); if(sc.hasOwn(data.animationData, 'destroyTime')){ - animation.destroyTime = data.animationData.destroyTime; + newAnimation.destroyTime = data.animationData.destroyTime; } if(sc.hasOwn(data.animationData, 'depthByPlayer')){ - animation.depthByPlayer = data.animationData.depthByPlayer; + newAnimation.depthByPlayer = data.animationData.depthByPlayer; } + this.gameManager.createdAnimations[animationCreateData.key] = newAnimation; + return this.gameManager.createdAnimations[animationCreateData.key]; } - prepareAnimationData(data, uiScene, direction = false) + prepareAnimationData(data, uiScene, direction = '') { // @NOTE: here we use have two keys, the animation key and the animationData.img, this is because we could have // a single sprite with multiple attacks, and use the start and end frame to run the required one. @@ -275,9 +247,9 @@ class PreloaderHandler return animationCreateData; } - getAnimationKey(data, direction = false) + getAnimationKey(data, direction = '') { - return (data.skillKey ? data.skillKey+'_' : '')+data.key+(direction ? '_'+direction : ''); + return (data.skillKey ? data.skillKey+'_' : '')+data.key+(direction && '' !== direction ? '_'+direction : ''); } } diff --git a/lib/actions/client/receiver-wrapper.js b/lib/actions/client/receiver-wrapper.js index fb43fc8e2..aaef470ee 100644 --- a/lib/actions/client/receiver-wrapper.js +++ b/lib/actions/client/receiver-wrapper.js @@ -156,9 +156,7 @@ class ReceiverWrapper extends Receiver targetSprite.moveSprites[hitAnimKey + '_' + targetSpriteId] = hitSprite; } let animData = allAnimations[hitAnimKey]; - let depth = 'above' === sc.get(animData.animationData, 'depthByPlayer', '') - ? targetSprite.depth + 100 - : targetSprite.depth - 0.1; + let depth = targetSprite.depth+('above' === sc.get(animData.animationData, 'depthByPlayer', '') ? 100 : -0.1); hitSprite.depthByPlayer = animData.animationData.depthByPlayer; hitSprite.setDepth(depth); return hitSprite; @@ -270,7 +268,10 @@ class ReceiverWrapper extends Receiver let sceneAnimation = currentScene.getAnimationByKey(animationKey); if(!sceneAnimation){ if(-1 === animationKey.indexOf('default')){ - Logger.error('Animation sprite not found', animationKey); + Logger.error( + 'Animation sprite not found for "'+animationKey+'".', + this.gameManager.config.client.skills.animations + ); } return false; } diff --git a/lib/actions/server/battle.js b/lib/actions/server/battle.js index b1a4d7e31..2e447a380 100644 --- a/lib/actions/server/battle.js +++ b/lib/actions/server/battle.js @@ -32,17 +32,20 @@ class Battle async runBattle(playerSchema, target) { if(GameConst.STATUS.ACTIVE !== playerSchema.inState){ - Logger.info('Battle inactive player.', playerSchema.inState); + Logger.error('Battle inactive player with ID "'+playerSchema.player_id+'".', playerSchema.inState); return false; } - if(target.inState && GameConst.STATUS.ACTIVE !== target.inState){ - Logger.info('Battle inactive target.', target.inState); + if(target.inState && GameConst.STATUS.ACTIVE.toString() !== target.inState.toString()){ + Logger.error( + 'Inactive target ID "'+(target.uid || target.player_id) + +'" in state "'+target.inState+'"/"'+GameConst.STATUS.ACTIVE+'".' + ); return false; } // @NOTE: each attack will have different properties to validate like range, delay, etc. let currentAction = this.getCurrentAction(playerSchema); if(!currentAction){ - Logger.error(['Actions not defined for this player.', 'ID:', playerSchema.player_id]); + Logger.error('Actions not defined for this player with ID "'+playerSchema.player_id+'".'); return false; } currentAction.currentBattle = this; @@ -92,6 +95,10 @@ class Battle async clientDeathUpdate(targetSchema, room, targetClient, affectedProperty) { + if(!targetSchema.player_id){ + Logger.error('Target is not a player.', targetSchema.player_id); + return false; + } targetSchema.inState = GameConst.STATUS.DEATH; let actionData = new BattleEndAction( targetSchema.state.x, @@ -105,14 +112,20 @@ class Battle await room.savePlayerState(targetSchema.sessionId); targetClient.send('*', {act: GameConst.GAME_OVER}); await room.savePlayerStats(targetSchema, targetClient); - setTimeout(async () => { - room.roomWorld.addBody(body); - targetSchema.inState = GameConst.STATUS.ACTIVE; - // player is dead! reinitialize the stats using its base value: - targetSchema.stats[affectedProperty] = targetSchema.statsBase[affectedProperty]; - await room.savePlayerStats(targetSchema, targetClient); - room.broadcast('*', {act: GameConst.REVIVED, t: targetSchema.sessionId}); - }, (room.config.get('server/players/gameOver/timeOut') || 1)); + targetSchema.setPrivate( + 'playerDeathTimer', + setTimeout( + async () => { + room.roomWorld.addBody(body); + targetSchema.inState = GameConst.STATUS.ACTIVE; + // player is dead! reinitialize the stats using its base value: + targetSchema.stats[affectedProperty] = targetSchema.statsBase[affectedProperty]; + await room.savePlayerStats(targetSchema, targetClient); + room.broadcast('*', {act: GameConst.REVIVED, t: targetSchema.sessionId}); + }, + (room.config.get('server/players/gameOver/timeOut') || 1) + ) + ); return false; } } diff --git a/lib/actions/server/event-listeners.js b/lib/actions/server/event-listeners.js index 7a084fa9b..760d5128a 100644 --- a/lib/actions/server/event-listeners.js +++ b/lib/actions/server/event-listeners.js @@ -5,7 +5,7 @@ */ const { SkillsEvents } = require('@reldens/skills'); -const { sc } = require('@reldens/utils'); +const { Logger, sc } = require('@reldens/utils'); class EventListeners { @@ -13,6 +13,10 @@ class EventListeners static async attachCastMovementEvents(props) { let {classPath, events, actionsPlugin} = props; + if(!classPath || !events || !actionsPlugin){ + Logger.critical('EventListeners: classPath, events or actionsPlugin undefined.', props); + return; + } let ownerId = classPath.getOwnerEventKey(); classPath.listenEvent( SkillsEvents.SKILL_BEFORE_CAST, @@ -22,7 +26,7 @@ class EventListeners } skill.owner.physicalBody.isBlocked = true; }, - 'skillBeforeCastPack', + classPath.getOwnerUniqueEventKey('skillBeforeCastPack'), ownerId ); classPath.listenEvent( @@ -33,7 +37,7 @@ class EventListeners } skill.owner.physicalBody.isBlocked = false; }, - 'skillAfterCastPack', + classPath.getOwnerUniqueEventKey('skillAfterCastPack'), ownerId ); await events.emit('reldens.actionsPrepareEventsListeners', actionsPlugin, classPath); diff --git a/lib/actions/server/player-enricher.js b/lib/actions/server/player-enricher.js index 44f88711c..f6d3d7c11 100644 --- a/lib/actions/server/player-enricher.js +++ b/lib/actions/server/player-enricher.js @@ -45,10 +45,13 @@ class PlayerEnricher currentPlayer.getSkillExtraData = (params) => { return SkillsExtraData.extractSkillExtraData(params); }; - currentPlayer.executePhysicalSkill = this.playerExecutePhysicalSkillCallback(currentPlayer, room); + currentPlayer.executePhysicalSkill = this.playerExecutePhysicalSkillCallback( + currentPlayer, + room.config.client.skills.animations + ); } - static playerExecutePhysicalSkillCallback(currentPlayer, room) + static playerExecutePhysicalSkillCallback(currentPlayer, skillsAnimationsData) { return async (target, executedSkill) => { let messageData = Object.assign({skillKey: executedSkill.key}, executedSkill.owner.getPosition()); @@ -64,7 +67,7 @@ class PlayerEnricher ); let from = {x: currentPlayer.state.x, y: currentPlayer.state.y}; executedSkill.initialPosition = from; - let animData = sc.get(room.config.client.skills.animations, executedSkill.key + '_bullet', false); + let animData = sc.get(skillsAnimationsData, executedSkill.key + '_bullet', false); if(animData){ executedSkill.animDir = sc.get(animData.animationData, 'dir', false); } diff --git a/lib/actions/server/plugin.js b/lib/actions/server/plugin.js index ff00e9a0d..3b6b77cde 100644 --- a/lib/actions/server/plugin.js +++ b/lib/actions/server/plugin.js @@ -36,39 +36,54 @@ class ActionsPlugin extends PluginInterface if(!this.events){ return false; } - this.events.on('reldens.serverReady', async (event) => { - await DataLoader.enrichConfig(event.serverManager.configManager, this.skillsModelsManager, this.dataServer); - }); - this.events.on('reldens.beforeSuperInitialGameData', async (superInitialGameData, roomGame) => { - await InitialGameDataEnricher.withClassPathLabels(roomGame, superInitialGameData); - await PlayerEnricher.withClassPath(roomGame, superInitialGameData, this.dataServer); - }); - this.events.on('reldens.roomsMessageActionsByRoom', async (roomMessageActions) => { - roomMessageActions.actions = new ActionsMessageActions(); + this.events.on('reldens.serverReady', this.serverReadyDataLoaderEnrichConfig.bind(this)); + this.events.on('reldens.beforeSuperInitialGameData', this.enrichInitialGameDataWithClassPathData.bind(this)); + this.events.on('reldens.roomsMessageActionsByRoom', this.appendRoomActions.bind(this)); + this.events.on('reldens.createdPlayerSchema', this.enrichPlayerWithSkillsAndActions.bind(this)); + this.events.on('reldens.createdNewPlayer', this.createPlayerClassPath.bind(this)); + } + + async serverReadyDataLoaderEnrichConfig(event) + { + await DataLoader.enrichConfig(event.serverManager.configManager, this.skillsModelsManager, this.dataServer); + } + + async enrichInitialGameDataWithClassPathData(superInitialGameData, roomGame) + { + await InitialGameDataEnricher.withClassPathLabels(roomGame, superInitialGameData); + await PlayerEnricher.withClassPath(roomGame, superInitialGameData, this.dataServer); + } + + async appendRoomActions(roomMessageActions) + { + roomMessageActions.actions = new ActionsMessageActions(); + } + + async enrichPlayerWithSkillsAndActions(client, userModel, currentPlayer, room) + { + await PlayerEnricher.withActions(currentPlayer, room, this.events); + await PlayerEnricher.withSkillsServerAndClassPath({ + client, + room, + skillsModelsManager: this.skillsModelsManager, + currentPlayer, + dataServer: this.dataServer, + events: this.events }); - this.events.on('reldens.createdPlayerSchema', async (client, userModel, currentPlayer, room) => { - await PlayerEnricher.withActions(currentPlayer, room, this.events); - await PlayerEnricher.withSkillsServerAndClassPath({ - client, - currentPlayer, - room, - skillsModelsManager: this.skillsModelsManager, - dataServer: this.dataServer, - events: this.events - }); - await EventListeners.attachCastMovementEvents({ - classPath: currentPlayer.skillsServer.classPath, - events: this.events, - actionsPlugin: this - }); + await EventListeners.attachCastMovementEvents({ + classPath: currentPlayer.skillsServer.classPath, + events: this.events, + actionsPlugin: this }); - this.events.on('reldens.createdNewPlayer', async (player, loginData, loginManager) => { - return PlayerClassPathHandler.createFromLoginData({ - loginManager, - loginData, - player, - dataServer: this.dataServer - }); + } + + async createPlayerClassPath(player, loginData, loginManager) + { + return PlayerClassPathHandler.createFromLoginData({ + loginManager, + loginData, + player, + dataServer: this.dataServer }); } diff --git a/lib/actions/server/pve.js b/lib/actions/server/pve.js index beb682216..f926a65c8 100644 --- a/lib/actions/server/pve.js +++ b/lib/actions/server/pve.js @@ -51,14 +51,23 @@ class Pve extends Battle 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(!this.targetObject){ + Logger.critical('Target Object reference removed.'); + return false; + } + let thisWorldKey = this.targetObject?.objectBody?.world?.worldKey; + let targetWorldKey = playerSchema?.physicalBody?.world?.worldKey; + if(!thisWorldKey || !targetWorldKey || thisWorldKey !== targetWorldKey){ + Logger.debug('World keys check failed.', {thisWorldKey, targetWorldKey}); + return false; + } if( !room?.roomWorld || !room?.state || !playerSchema || !room.playerBySessionIdFromState(playerSchema.sessionId) ){ + Logger.debug('Room or player missing references.'); // @NOTE: leaveBattle is used for when the player can't be reached anymore or disconnected. this.leaveBattle(playerSchema); return false; @@ -156,13 +165,18 @@ class Pve extends Battle leaveBattle(playerSchema) { this.removeInBattlePlayer(playerSchema); + if(!this.targetObject){ + Logger.critical('Target Object reference not found.'); + return false; + } this.targetObject.objectBody.moveToOriginalPoint(); } async battleEnded(playerSchema, room) { // @TODO - BETA - Implement battle end in both PvE and PvP. - this.targetObject.inState = GameConst.STATUS.DEATH; + this.targetObject.objectBody.bodyState.inState = GameConst.STATUS.DEATH; + Logger.debug('Battle ended, set target with ID "'+this.targetObject.uid+'" state to "DEATH".'); this.removeInBattlePlayer(playerSchema); let actionData = new BattleEndAction( this.targetObject.objectBody.position[0], diff --git a/lib/actions/server/skills/skills-extra-data.js b/lib/actions/server/skills/skills-extra-data.js index 54f04bf3d..eb8c0d8b9 100644 --- a/lib/actions/server/skills/skills-extra-data.js +++ b/lib/actions/server/skills/skills-extra-data.js @@ -9,6 +9,7 @@ const { sc } = require('@reldens/utils'); class SkillsExtraData { + static extractSkillExtraData(params) { let extraData = {}; diff --git a/lib/actions/server/skills/type-attack.js b/lib/actions/server/skills/type-attack.js index 8d3c66c12..b8a509f14 100644 --- a/lib/actions/server/skills/type-attack.js +++ b/lib/actions/server/skills/type-attack.js @@ -13,6 +13,7 @@ class TypeAttack extends Attack constructor(props) { super(props); + // @TODO - BETA - Refactor and extract room reference. this.room = false; this.currentBattle = false; } diff --git a/lib/actions/server/skills/type-effect.js b/lib/actions/server/skills/type-effect.js index 1d4a424e5..6b8021d78 100644 --- a/lib/actions/server/skills/type-effect.js +++ b/lib/actions/server/skills/type-effect.js @@ -13,6 +13,7 @@ class TypeEffect extends Effect constructor(props) { super(props); + // @TODO - BETA - Refactor and extract room reference. this.room = false; this.currentBattle = false; } diff --git a/lib/actions/server/skills/type-physical-attack.js b/lib/actions/server/skills/type-physical-attack.js index e21b97a1f..b751736d4 100644 --- a/lib/actions/server/skills/type-physical-attack.js +++ b/lib/actions/server/skills/type-physical-attack.js @@ -13,6 +13,7 @@ class TypePhysicalAttack extends PhysicalAttack constructor(props) { super(props); + // @TODO - BETA - Refactor and extract room reference. this.room = false; this.currentBattle = false; // @NOTE: hit priority is something specifically from reldens physics engine, in order to change this value you @@ -87,6 +88,9 @@ class TypePhysicalAttack extends PhysicalAttack async restartBattle(validDefender) { if(!this.validateTargetOnHit && sc.hasOwn(validDefender, 'battle')){ + if(!validDefender.battle){ + return; + } // if target validation is disabled then any target could start the battle (pve): validDefender.battle.targetObject = validDefender; await validDefender.battle.startBattleWith(this.owner, this.room); @@ -126,7 +130,7 @@ class TypePhysicalAttack extends PhysicalAttack { body.world.removeBodies.push(body); // @TODO - BETA - Refactor and extract Colyseus into a driver. - this.room.state.bodies.delete(this.key+'_bullet_'+body.id); + this.room.state.removeBody(this.key+'_bullet_'+body.id); } } diff --git a/lib/actions/server/skills/type-physical-effect.js b/lib/actions/server/skills/type-physical-effect.js index 223770d03..6979ae122 100644 --- a/lib/actions/server/skills/type-physical-effect.js +++ b/lib/actions/server/skills/type-physical-effect.js @@ -13,6 +13,7 @@ class TypePhysicalEffect extends PhysicalEffect constructor(props) { super(props); + // @TODO - BETA - Refactor and extract room reference. this.room = false; this.currentBattle = false; // @NOTE: hit priority is something specifically from reldens physics engine, in order to change this value you @@ -76,6 +77,9 @@ class TypePhysicalEffect extends PhysicalEffect async restartBattle(validDefender) { if(!this.validateTargetOnHit && sc.hasOwn(validDefender, 'battle')){ + if(!validDefender.battle){ + return; + } // if target validation is disabled then any target could start the battle (pve): validDefender.battle.targetObject = validDefender; await validDefender.battle.startBattleWith(this.owner, this.room); @@ -112,7 +116,7 @@ class TypePhysicalEffect extends PhysicalEffect { body.world.removeBodies.push(body); // @TODO - BETA - Refactor and extract Colyseus into a driver. - this.room.state.bodies.delete(this.key+'_bullet_'+body.id); + this.room.state.removeBody(this.key+'_bullet_'+body.id); } } diff --git a/lib/ads/client/plugin.js b/lib/ads/client/plugin.js index e9a7bc0dd..266f3c41e 100644 --- a/lib/ads/client/plugin.js +++ b/lib/ads/client/plugin.js @@ -52,13 +52,13 @@ class AdsPlugin extends PluginInterface let providers = sc.get(this.config, 'providers', {}); let providersKeys = Object.keys(providers); if(0 === providersKeys.length){ - Logger.info('None ads providers configured.', this.config); + Logger.debug('None ads providers configured.', this.config); return false; } for(let i of providersKeys){ let provider = providers[i]; if(!provider.enabled){ - Logger.info({'Provider disabled': providers}); + Logger.debug({'Provider disabled': providers}); continue; } provider.classDefinition = sc.get(ProvidersList, i, false); diff --git a/lib/ads/client/sdk-handler.js b/lib/ads/client/sdk-handler.js index 308b4782e..3f701b089 100644 --- a/lib/ads/client/sdk-handler.js +++ b/lib/ads/client/sdk-handler.js @@ -21,12 +21,12 @@ class SdkHandler return false; } if(!sc.isObject(providers)){ - Logger.info('Providers not available.'); + Logger.debug('Providers not available.'); return false; } let keys = Object.keys(providers); if(0 === keys.length){ - Logger.info('Providers not found.'); + Logger.debug('Providers not found.'); return false; } for(let i of keys){ @@ -41,7 +41,7 @@ class SdkHandler { let sdkUrl = sc.get(provider, 'sdkUrl', ''); if('' === sdkUrl){ - Logger.info('Provider does not have an SDK URL.', provider); + Logger.debug('Provider does not have an SDK URL.', provider); return false; } let body = this.gameDom.getElement('body'); diff --git a/lib/chat/server/event-listener/npc-skills.js b/lib/chat/server/event-listener/npc-skills.js index 15244ae60..f31c47b51 100644 --- a/lib/chat/server/event-listener/npc-skills.js +++ b/lib/chat/server/event-listener/npc-skills.js @@ -4,11 +4,11 @@ * */ -const { SkillsEvents, SkillConst } = require('@reldens/skills'); -const { sc } = require('@reldens/utils'); const { NpcDamageCallback } = require('../messages/npc-damage-callback'); const { NpcModifiersCallback } = require('../messages/npc-modifiers-callback'); const { NpcDodgeCallback } = require('../messages/npc-dodge-callback'); +const { SkillsEvents, SkillConst } = require('@reldens/skills'); +const { sc } = require('@reldens/utils'); class NpcSkills { @@ -16,8 +16,8 @@ class NpcSkills static listenEvents(props, chatConfig, chatManager) { let skillsByType = this.fetchSkillsByType(props, chatConfig); - let attackSkill = sc.get(skillsByType, SkillConst.SKILL_TYPE_ATTACK, null); - let effectSkill = sc.get(skillsByType, SkillConst.SKILL_TYPE_EFFECT, null); + let attackSkill = sc.get(skillsByType, SkillConst.SKILL.TYPE.ATTACK, null); + let effectSkill = sc.get(skillsByType, SkillConst.SKILL.TYPE.EFFECT, null); this.listenDamageEvent(attackSkill, chatConfig, chatManager); this.listenModifiersEvent(effectSkill, chatConfig, chatManager); this.listenAfterRunLogicEvent((attackSkill || effectSkill), chatConfig, chatManager); @@ -36,7 +36,8 @@ class NpcSkills } await NpcDamageCallback.sendMessage({skill, target, damage, chatManager}); }, - 'skillAttackApplyDamageChat', + attackSkill.getOwnerUniqueEventKey('skillAttackApplyDamageChat'), + // @NOTE: objects ownerIdProperty is their uid and that's used as master key for the object event listeners. attackSkill.owner[attackSkill.ownerIdProperty] ); } @@ -51,7 +52,8 @@ class NpcSkills async (skill) => { await NpcModifiersCallback.sendMessage({skill, chatManager}); }, - 'skillApplyModifiersChat', + effectSkill.getOwnerUniqueEventKey('skillApplyModifiersChat'), + // @NOTE: objects ownerIdProperty is their uid and that's used as master key for the object event listeners. effectSkill.owner[effectSkill.ownerIdProperty] ); } @@ -69,7 +71,8 @@ class NpcSkills } await NpcDodgeCallback.sendMessage({skill, chatManager}); }, - 'skillDodgeChat', + skillForLogic.getOwnerUniqueEventKey('skillDodgeChat'), + // @NOTE: objects ownerIdProperty is their uid and that's used as master key for the object event listeners. skillForLogic.owner[skillForLogic.ownerIdProperty] ); } @@ -87,11 +90,11 @@ class NpcSkills if(sc.hasOwn(skillsByType, skill.type)){ continue; } - if(SkillConst.SKILL_TYPE_ATTACK === skill.type || SkillConst.SKILL_TYPE_ATTACK === skill.parentType){ - skillsByType[SkillConst.SKILL_TYPE_ATTACK] = skill; + if(SkillConst.SKILL.TYPE.ATTACK === skill.type || SkillConst.SKILL.TYPE.ATTACK === skill.parentType){ + skillsByType[SkillConst.SKILL.TYPE.ATTACK] = skill; } - if(SkillConst.SKILL_TYPE_EFFECT === skill.type || SkillConst.SKILL_TYPE_EFFECT === skill.parentType){ - skillsByType[SkillConst.SKILL_TYPE_EFFECT] = skill; + if(SkillConst.SKILL.TYPE.EFFECT === skill.type || SkillConst.SKILL.TYPE.EFFECT === skill.parentType){ + skillsByType[SkillConst.SKILL.TYPE.EFFECT] = skill; } let totalValidTypes = sc.get(chatConfig, 'totalValidTypes', 2); if(totalValidTypes === Object.keys(skillsByType).length){ diff --git a/lib/chat/server/event-listener/player-skills.js b/lib/chat/server/event-listener/player-skills.js index 560714b0a..478914e4c 100644 --- a/lib/chat/server/event-listener/player-skills.js +++ b/lib/chat/server/event-listener/player-skills.js @@ -4,10 +4,10 @@ * */ -const { SkillsEvents, SkillConst } = require('@reldens/skills'); const { PlayerDamageCallback } = require('../messages/player-damage-callback'); const { PlayerModifiersCallback } = require('../messages/player-modifiers-callback'); const { PlayerDodgeCallback } = require('../messages/player-dodge-callback'); +const { SkillsEvents, SkillConst } = require('@reldens/skills'); class PlayerSkills { @@ -38,8 +38,8 @@ class PlayerSkills chatManager: chatManager }); }, - 'skillAttackApplyDamageChat', - classPath.owner[classPath.ownerIdProperty] + classPath.getOwnerUniqueEventKey('skillAttackApplyDamageChat'), + classPath.getOwnerEventKey() ); } @@ -57,8 +57,8 @@ class PlayerSkills chatManager: chatManager }); }, - 'skillApplyModifiersChat', - classPath.owner[classPath.ownerIdProperty] + classPath.getOwnerUniqueEventKey('skillApplyModifiersChat'), + classPath.getOwnerEventKey() ); } @@ -79,8 +79,8 @@ class PlayerSkills chatManager: chatManager }); }, - 'skillDodgeChat', - classPath.owner[classPath.ownerIdProperty] + classPath.getOwnerUniqueEventKey('skillDodgeChat'), + classPath.getOwnerEventKey() ); } diff --git a/lib/chat/server/messages/npc-modifiers-callback.js b/lib/chat/server/messages/npc-modifiers-callback.js index 7e0aad01b..f0ed0c1a2 100644 --- a/lib/chat/server/messages/npc-modifiers-callback.js +++ b/lib/chat/server/messages/npc-modifiers-callback.js @@ -39,7 +39,7 @@ class NpcModifiersCallback for(let i of appliedModifiersKeys){ let value = lastAppliedModifiers[i]; let property = i.split('/').pop(); - messageData.push({[property]: value}); + messageData[property] = value; } let isObjectOwner = sc.hasOwn(skill.owner, 'key'); let from = isObjectOwner ? skill.owner.title : skill.owner.playerName; diff --git a/lib/chat/server/messages/player-modifiers-callback.js b/lib/chat/server/messages/player-modifiers-callback.js index eb0a960bb..b0339c321 100644 --- a/lib/chat/server/messages/player-modifiers-callback.js +++ b/lib/chat/server/messages/player-modifiers-callback.js @@ -34,7 +34,7 @@ class PlayerModifiersCallback for(let i of appliedModifiersKeys){ let value = lastAppliedModifiers[i]; let property = i.split('/').pop(); - messageData.push({[property]: value}); + messageData[property] = value; } let messageObject = MessageFactory.create(ChatConst.TYPES.SKILL, message, messageData, skill.owner.playerName); client.send(messageObject); diff --git a/lib/game/client/room-events.js b/lib/game/client/room-events.js index 2535c6366..98889ca5e 100644 --- a/lib/game/client/room-events.js +++ b/lib/game/client/room-events.js @@ -159,7 +159,9 @@ class RoomEvents { // do not move the player if it is changing the scene: if(player.state.scene !== this.roomName){ - Logger.info('Player scene miss match.', {playerScene: player.state.scene, currentScene: this.roomName}); + if(!this.gameManager.isChangingScene){ + Logger.info('Player scene miss match.', {playerScene: player.state.scene, currentScene: this.roomName}); + } return; } let currentScene = this.getActiveScene(); diff --git a/lib/game/client/scene-dynamic.js b/lib/game/client/scene-dynamic.js index 366887f87..5235463ad 100644 --- a/lib/game/client/scene-dynamic.js +++ b/lib/game/client/scene-dynamic.js @@ -304,10 +304,7 @@ class SceneDynamic extends Scene } let newX = Phaser.Math.Linear(entity.x, (entityState.x - this.player.leftOff), this.interpolationSpeed); let newY = Phaser.Math.Linear(entity.y, (entityState.y - this.player.topOff), this.interpolationSpeed); - this.player.updateSpritePosition(entity, newX, newY); - this.player.playPlayerAnimation(entity, Object.assign({}, entityState, {x: newX, y: newY})); - this.player.stopPlayerAnimation(entity, entityState); - this.player.updatePlayerState(entity, entity, i); + this.player.processPlayerPositionAnimationUpdate(entity, entityState, i, newX, newY); delete this.interpolatePlayersPosition[i]; } } @@ -499,7 +496,8 @@ class SceneDynamic extends Scene getAnimationByKey(key) { - if(!this.anims || !this.anims.anims || !this.anims.anims.entries){ + if(!this.anims || !this.anims?.anims || !this.anims?.anims?.entries){ + Logger.error('Animations not loaded.', this.anims); return false; } return sc.get(this.anims.anims.entries, key, false); diff --git a/lib/game/client/scene-preloader.js b/lib/game/client/scene-preloader.js index efe00d281..b0c8a2e9c 100644 --- a/lib/game/client/scene-preloader.js +++ b/lib/game/client/scene-preloader.js @@ -32,6 +32,9 @@ class ScenePreloader extends Scene this.preloadAssets = props.preloadAssets; this.directionalAnimations = {}; this.objectsAnimations = {}; + if(!this.gameManager.createdAnimations){ + this.gameManager.createdAnimations = {}; + } let currentScene = this.gameManager.activeRoomEvents.getActiveScene(); currentScene.objectsAnimationsData = props.objectsAnimationsData; this.playerSpriteSize = { @@ -77,8 +80,10 @@ class ScenePreloader extends Scene if(!this.gameManager.config.get('client/ui/pointer/show')){ return; } - // @TODO - BETA - Make pointer sprite data configurable. - let pointerData = {frameWidth: 32, frameHeight: 32}; + let pointerData = { + frameWidth: this.gameManager.config.getWithoutLogs('client/general/assets/arrowDownFrameWidth', 32), + frameHeight: this.gameManager.config.getWithoutLogs('client/general/assets/arrowDownFrameHeight', 32) + }; this.load.spritesheet( GameConst.ARROW_DOWN, this.gameManager.config.get('client/general/assets/arrowDownPath', '/assets/sprites/arrow-down.png'), @@ -363,13 +368,17 @@ class ScenePreloader extends Scene createAnimationWith(anim) { - this.anims.create({ + if(this.gameManager.createdAnimations[anim.k]){ + return; + } + this.gameManager.createdAnimations[anim.k] = this.anims.create({ key: anim.k, frames: this.anims.generateFrameNumbers(anim.img, {start: anim.start, end: anim.end}), frameRate: sc.hasOwn(anim, 'rate') ? anim.rate : this.configuredFrameRate, repeat: anim.repeat, hideOnComplete: sc.hasOwn(anim, 'hide') ? anim.hide : true, }); + return this.gameManager.createdAnimations[anim.k]; } registerControllers(controllersBox) diff --git a/lib/game/server/install-templates/.env.dist b/lib/game/server/install-templates/.env.dist index a27f28b8c..19827db16 100644 --- a/lib/game/server/install-templates/.env.dist +++ b/lib/game/server/install-templates/.env.dist @@ -37,8 +37,8 @@ RELDENS_DB_LIMIT=0 # If you don't want to specify the string you can pass options to be append to the automatically generated string. # RELDENS_DB_CONNECT_STRING_OPTIONS=authSource=reldens&readPreference=primary&appname=MongoDB%20Compass&ssl=false # Logs: -RELDENS_LOG_LEVEL=9 -RELDENS_ENABLE_TRACE_FOR=emergency,alert,critical,error,warning +RELDENS_LOG_LEVEL=7 +RELDENS_ENABLE_TRACE_FOR=emergency,alert,critical # Mailer: RELDENS_MAILER_ENABLE={{&mailer-enable}} RELDENS_MAILER_SERVICE={{&mailer-service}} diff --git a/lib/game/server/theme-manager.js b/lib/game/server/theme-manager.js index 065753bb3..75fc9db12 100644 --- a/lib/game/server/theme-manager.js +++ b/lib/game/server/theme-manager.js @@ -365,7 +365,7 @@ class ThemeManager sourceMaps: false, distEntry: entryPath, distDir: this.distPath, - isLibrary: true, + isLibrary: false, outputFormat: 'esmodule' } }; diff --git a/lib/inventory/client/inventory-receiver.js b/lib/inventory/client/inventory-receiver.js index c9ee315f6..e15c1783c 100644 --- a/lib/inventory/client/inventory-receiver.js +++ b/lib/inventory/client/inventory-receiver.js @@ -20,6 +20,7 @@ class InventoryReceiver extends Receiver super(props); this.gameManager = props.gameManager; this.itemSprites = {}; + this.itemsAnimations = {}; } onExecuting(message) @@ -27,16 +28,28 @@ class InventoryReceiver extends Receiver // @TODO - BETA - Improve, split in several classes, methods and functionalities. let item = message.item; if(!sc.hasOwn(item, 'animationData')){ + Logger.warning('Item does not contain animation data.', message); return false; } let animKey = InventoryConst.ANIMATION_KEY_PREFIX+item.key; let currentScene = this.gameManager.getActiveScene(); + let existentAnimation = this.itemSprites[animKey] + && this.itemSprites[animKey].anims + && currentScene.anims.get(animKey); + if(existentAnimation){ + Logger.debug('Animation already exists, playing: '+animKey); + this.playSpriteAnimation(animKey, item); + return false; + } // @TODO - BETA - Remove hardcoded file extension. currentScene.load.spritesheet(animKey, '/assets/custom/sprites/'+item.key+GameConst.FILES.EXTENSIONS.PNG, { frameWidth: item.animationData.frameWidth || 64, frameHeight: item.animationData.frameHeight || 64 + }).on('loaderror', (event) => { + Logger.error('Sprite load error: '+animKey, event); }); currentScene.load.on('complete', () => { + Logger.debug('Scene load complete, playing: '+animKey); this.createItemSprites(animKey, item, message, currentScene); }); currentScene.load.start(); @@ -44,14 +57,6 @@ class InventoryReceiver extends Receiver createItemSprites(animKey, item, message, currentScene) { - if(this.itemSprites[animKey] && sc.hasOwn(this.itemSprites, animKey)){ - if(!item.animationData.destroyOnComplete){ - this.itemSprites[animKey].anims.play(animKey, false); - return; - } - Logger.info('Sprite already running for item: '+animKey); - return false; - } let targetId = this.extractTargetId(item, message, currentScene); if(!targetId){ Logger.error('Target ID not found.'); @@ -63,51 +68,74 @@ class InventoryReceiver extends Receiver return false; } // @TODO - BETA - Make all the defaults configurable. - currentScene.anims.create({ - key: animKey, - frames: currentScene.anims.generateFrameNumbers(animKey, { - start: item.animationData.start || 0, - end: item.animationData.end || 1 - }), - frameRate: sc.get(item.animationData, 'rate', currentScene.configuredFrameRate), - repeat: item.animationData.repeat || 3, - hideOnComplete: sc.get(item.animationData, 'hide', true), - }); - if(item.animationData.closeInventoryOnUse){ - this.gameManager.gameDom.getElement('#inventory-close').click(); + let animationFromScene = currentScene.anims.get(animKey); + if(!animationFromScene){ + Logger.debug('Creating new animation on scene: '+animKey); + animationFromScene = currentScene.anims.create({ + key: animKey, + frames: currentScene.anims.generateFrameNumbers(animKey, { + start: item.animationData.start || 0, + end: item.animationData.end || 1 + }), + frameRate: sc.get(item.animationData, 'rate', currentScene.configuredFrameRate), + repeat: item.animationData.repeat || 3, + hideOnComplete: sc.get(item.animationData, 'hide', true), + showOnStart: sc.get(item.animationData, 'showOnStart', true), + }); } + this.itemsAnimations[animKey] = animationFromScene; let x = sc.get(item.animationData, 'fixedX', (item.animationData.usePlayerPosition ? playerSprite.x : 0)); let y = sc.get(item.animationData, 'fixedY', (item.animationData.usePlayerPosition ? playerSprite.y : 0)); this.itemSprites[animKey] = currentScene.physics.add.sprite(x, y, animKey); - this.itemSprites[animKey].setDepth(1000000); + this.itemSprites[animKey] = this.itemSprites[animKey].setDepth(90000); + this.itemSprites[animKey].depthByPlayer = 'above'; if(item.animationData.followPlayer){ playerSprite.moveSprites[animKey] = this.itemSprites[animKey]; } // @TODO - BETA - Make auto-destroy configurable. - this.itemSprites[animKey].anims.play(animKey, false).on('animationcomplete', () => { - this.destroyAnimation(item, animKey, playerSprite); + Logger.debug('Playing sprite: '+animKey); + this.playSpriteAnimation(animKey, item).on('animationcomplete', () => { + if(item.animationData.destroyOnComplete){ + this.destroyAnimation(item, animKey, playerSprite); + } }); } - extractTargetId(item, message, currentScene) + playSpriteAnimation(animKey, item) { - if(item.animationData.startsOnTarget && message.target?.playerId){ - return message.target.playerId; + // @TODO - BETA - Make closeInventoryOnUse and ignoreIfPlaying default values configurable. + let closeInventoryOnUse = sc.get(item.animationData, 'closeInventoryOnUse', false); + if(closeInventoryOnUse){ + this.gameManager.gameDom.getElement('#inventory-close')?.click(); } - return currentScene.player?.playerId || false; + let spriteAnims = this.itemSprites[animKey].anims; + if(!spriteAnims){ + Logger.error('Sprite animation not found: '+animKey); + return false; + } + spriteAnims.visible = true; + return spriteAnims.play(animKey, sc.get(item.animationData, 'ignoreIfPlaying', true)); } destroyAnimation(item, animKey, playerSprite) { - if(!item.animationData.destroyOnComplete){ - return; - } this.itemSprites[animKey].destroy(); delete this.itemSprites[animKey]; + delete this.itemsAnimations[animKey]; if(item.animationData.followPlayer){ delete playerSprite.moveSprites[animKey]; } + Logger.debug('Animation and sprite destroyed: '+animKey); } + + extractTargetId(item, message, currentScene) + { + if(item.animationData.startsOnTarget && message.target?.playerId){ + return message.target.playerId; + } + return currentScene.player?.playerId || false; + } + } module.exports.InventoryReceiver = InventoryReceiver; diff --git a/lib/inventory/client/plugin.js b/lib/inventory/client/plugin.js index 9c8f490f0..3e572b415 100644 --- a/lib/inventory/client/plugin.js +++ b/lib/inventory/client/plugin.js @@ -14,9 +14,9 @@ const { TemplatesHandler } = require('./templates-handler'); const { TranslationsMapper } = require('../../snippets/client/translations-mapper'); const Translations = require('./snippets/en_US'); const { InventoryConst } = require('../constants'); +const { ItemsEvents, ItemsConst } = require('@reldens/items-system'); const { GameConst } = require('../../game/constants'); const { Logger, sc } = require('@reldens/utils'); -const { ItemsEvents, ItemsConst } = require('@reldens/items-system'); class InventoryPlugin extends PluginInterface { @@ -160,49 +160,84 @@ class InventoryPlugin extends PluginInterface listenInventoryEvents(uiScene, inventoryPanel, equipmentPanel) { let gameManager = uiScene.gameManager; - let masterKey = 'p'+gameManager.inventory.manager.getOwnerId(); - gameManager.inventory.manager.listenEvent(ItemsEvents.ADD_ITEM, (inventory, item) => { - let output = this.createItemBox(item, 'inventoryItem', gameManager, uiScene); - gameManager.gameDom.appendToElement('#'+InventoryConst.INVENTORY_ITEMS, output); - this.setupButtonsActions(inventoryPanel, item.getInventoryId(), item, uiScene); - }, 'addItemPack', masterKey); - gameManager.inventory.manager.listenEvent(ItemsEvents.SET_ITEMS, (props) => { - inventoryPanel.innerHTML = ''; - for(let i of Object.keys(props.items)){ - let item = props.items[i]; - this.displayItem(item, uiScene, equipmentPanel, inventoryPanel, i); - } - }, 'setItemsPack', masterKey); - gameManager.inventory.manager.listenEvent(ItemsEvents.MODIFY_ITEM_QTY, (item) => { - let qtyBox = uiScene.getUiElement('inventory').getChildByID('item-qty-'+item.getInventoryId()); - qtyBox.innerHTML = item.qty; - }, 'modifyItemQtyPack', masterKey); - gameManager.inventory.manager.listenEvent(ItemsEvents.REMOVE_ITEM, (inventory, itemKey) => { - uiScene.getUiElement('inventory').getChildByID('item-'+itemKey).remove(); - }, 'removeItemPack', masterKey); - gameManager.inventory.manager.listenEvent(ItemsEvents.SET_GROUPS, (props) => { - // @TODO - BETA - If groups are re-set or updated we will need to update the items as well. - let reEquipItems = false; - let equipmentItemsGroups = gameManager.gameDom.getElement('#'+InventoryConst.EQUIPMENT_ITEMS); - if(equipmentItemsGroups.innerHTML !== ''){ - reEquipItems = true; - } - equipmentItemsGroups.innerHTML = ''; - let orderedGroups = this.sortGroups(props.groups); - for(let i of orderedGroups){ - let output = this.createGroupBox(props.groups[i], gameManager, uiScene); - gameManager.gameDom.appendToElement('#'+InventoryConst.EQUIPMENT_ITEMS, output); - } - if(reEquipItems){ - this.resetEquippedItemsDisplay(gameManager, uiScene, equipmentPanel, inventoryPanel); - } - }, 'setGroupsPack', masterKey); - gameManager.inventory.manager.listenEvent(ItemsEvents.EQUIP_ITEM, (item) => { - this.displayItem(item, uiScene, equipmentPanel, inventoryPanel, item.getInventoryId()); - }, 'equipItemPack', masterKey); - gameManager.inventory.manager.listenEvent(ItemsEvents.UNEQUIP_ITEM, (item) => { - this.displayItem(item, uiScene, equipmentPanel, inventoryPanel, item.getInventoryId()); - }, 'unequipItemPack', masterKey); + let masterKey = gameManager.inventory.manager.getOwnerEventKey(); + gameManager.inventory.manager.listenEvent( + ItemsEvents.ADD_ITEM, + (inventory, item) => { + let output = this.createItemBox(item, 'inventoryItem', gameManager, uiScene); + gameManager.gameDom.appendToElement('#'+InventoryConst.INVENTORY_ITEMS, output); + this.setupButtonsActions(inventoryPanel, item.getInventoryId(), item, uiScene); + }, + gameManager.inventory.manager.getOwnerUniqueEventKey('addItemPack'), + masterKey + ); + gameManager.inventory.manager.listenEvent( + ItemsEvents.SET_ITEMS, + (props) => { + inventoryPanel.innerHTML = ''; + for(let i of Object.keys(props.items)){ + let item = props.items[i]; + this.displayItem(item, uiScene, equipmentPanel, inventoryPanel, i); + } + }, + gameManager.inventory.manager.getOwnerUniqueEventKey('setItemsPack'), + masterKey + ); + gameManager.inventory.manager.listenEvent( + ItemsEvents.MODIFY_ITEM_QTY, + (item) => { + let qtyBox = uiScene.getUiElement('inventory').getChildByID('item-qty-'+item.getInventoryId()); + qtyBox.innerHTML = item.qty; + }, + gameManager.inventory.manager.getOwnerUniqueEventKey('modifyItemQtyPack'), + masterKey + ); + gameManager.inventory.manager.listenEvent( + ItemsEvents.REMOVE_ITEM, + (inventory, itemKey) => { + uiScene.getUiElement('inventory').getChildByID('item-'+itemKey).remove(); + }, + gameManager.inventory.manager.getOwnerUniqueEventKey('removeItemPack'), + masterKey + ); + gameManager.inventory.manager.listenEvent( + ItemsEvents.SET_GROUPS, + (props) => { + // @TODO - BETA - If groups are re-set or updated we will need to update the items as well. + let reEquipItems = false; + let equipmentItemsGroups = gameManager.gameDom.getElement('#'+InventoryConst.EQUIPMENT_ITEMS); + if(equipmentItemsGroups.innerHTML !== ''){ + reEquipItems = true; + } + equipmentItemsGroups.innerHTML = ''; + let orderedGroups = this.sortGroups(props.groups); + for(let i of orderedGroups){ + let output = this.createGroupBox(props.groups[i], gameManager, uiScene); + gameManager.gameDom.appendToElement('#'+InventoryConst.EQUIPMENT_ITEMS, output); + } + if(reEquipItems){ + this.resetEquippedItemsDisplay(gameManager, uiScene, equipmentPanel, inventoryPanel); + } + }, + gameManager.inventory.manager.getOwnerUniqueEventKey('setGroupsPack'), + masterKey + ); + gameManager.inventory.manager.listenEvent( + ItemsEvents.EQUIP_ITEM, + (item) => { + this.displayItem(item, uiScene, equipmentPanel, inventoryPanel, item.getInventoryId()); + }, + gameManager.inventory.manager.getOwnerUniqueEventKey('equipItemPack'), + masterKey + ); + gameManager.inventory.manager.listenEvent( + ItemsEvents.UNEQUIP_ITEM, + (item) => { + this.displayItem(item, uiScene, equipmentPanel, inventoryPanel, item.getInventoryId()); + }, + gameManager.inventory.manager.getOwnerUniqueEventKey('unequipItemPack'), + masterKey + ); } resetEquippedItemsDisplay(gameManager, uiScene, equipmentPanel, inventoryPanel) diff --git a/lib/objects/client/animation-engine.js b/lib/objects/client/animation-engine.js index 9afbafa31..ef5dd090d 100644 --- a/lib/objects/client/animation-engine.js +++ b/lib/objects/client/animation-engine.js @@ -4,14 +4,14 @@ * * Objects flow: * - * When you create an NpcObject this can/should be set as "interactive", around line 92, after the validation + * When you create an NpcObject this can/should be set as "interactive", after the validation * if(this.isInteractive){ * This will activate the onpointerdown event so when you click on the object it will send the action * ObjectsConst.OBJECT_INTERACTION * Along with its own ID and type. - * The server will pick up this information and validate it on the NpcObject.executeMessageActions method around - * line 60, and return a UI message to open a UI dialog box, updated with the information coming in the message, see - * RoomEvents.initUi method. + * The server will pick up this information and validate it on the NpcObject.executeMessageActions method, and return + * a UI message to open a UI dialog box, updated with the information coming in the message. + * See RoomEvents.initUi method. * */ @@ -25,6 +25,7 @@ class AnimationEngine constructor(gameManager, props, currentPreloader) { this.currentPreloader = currentPreloader; + this.currentAnimation = false; this.gameManager = gameManager; this.enabled = props.enabled || false; this.key = props.key; @@ -40,6 +41,9 @@ class AnimationEngine this.y = props.y || 0; this.repeat = isNaN(props.repeat) ? -1 : props.repeat; this.hideOnComplete = props.hideOnComplete || false; + if(!this.gameManager.createdAnimations){ + this.gameManager.createdAnimations = {}; + } // @NOTE: you cannot combine destroyOnComplete with repeat = -1, because an animation with infinite // repetitions will never trigger the complete event. this.destroyOnComplete = props.destroyOnComplete || false; @@ -109,19 +113,24 @@ class AnimationEngine if(this.zeroPad !== false){ animationData.zeroPad = this.zeroPad; } - this.frameNumbers = this.currentPreloader.anims.generateFrameNumbers(this.asset_key, animationData); + let frameNumbers = this.currentPreloader.anims.generateFrameNumbers(this.asset_key, animationData); let createData = { key: this.key, - frames: this.frameNumbers, + frames: frameNumbers, frameRate: this.frameRate, repeat: this.repeat, hideOnComplete: this.hideOnComplete }; - this.currentAnimation = this.currentPreloader.anims.create(createData); + this.currentAnimation = this.gameManager.createdAnimations[this.key]; + if(!this.currentAnimation){ + Logger.debug('Creating animation: '+this.key); + this.currentAnimation = this.currentPreloader.anims.create(createData); + } this.currentPreloader.objectsAnimations[this.key] = this.currentAnimation; + this.gameManager.createdAnimations[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 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); @@ -144,7 +153,7 @@ class AnimationEngine return; } this.sceneSprite.on('animationcomplete', () => { - this.currentAnimation.destroy(); + this.currentAnimation?.destroy(); this.sceneSprite.destroy(); }, this); } @@ -220,7 +229,12 @@ class AnimationEngine return; } for(let i of animationsKeys){ + if(this.gameManager.createdAnimations[i]){ + this.currentPreloader.objectsAnimations[i] = this.gameManager.createdAnimations[i]; + continue; + } if(sc.hasOwn(this.currentPreloader.objectsAnimations, i)){ + // @TODO - BETA - Clean up, can objectsAnimations be removed? continue; } let animData = animations[i]; @@ -239,6 +253,7 @@ class AnimationEngine asset_key: animData['asset_key'] || this.asset_key }; this.currentPreloader.objectsAnimations[i] = this.currentPreloader.anims.create(createData); + this.gameManager.createdAnimations[i] = this.currentPreloader.objectsAnimations[i]; } } diff --git a/lib/objects/client/plugin.js b/lib/objects/client/plugin.js index e9fde1bbe..16f528eb5 100644 --- a/lib/objects/client/plugin.js +++ b/lib/objects/client/plugin.js @@ -113,8 +113,9 @@ class ObjectsPlugin extends PluginInterface Logger.warning('Could not create bullet sprite.', currentScene); return false; } - bulletSprite.setDepth(300000); + bulletSprite.setDepth(11000); this.bullets[key] = bulletSprite; + Logger.debug({createdBulletSprite: skillBullet, shootFrom: body, bulletSprite}); } setOnChangeBodyCallback(body, key, room, gameManager) @@ -217,7 +218,7 @@ class ObjectsPlugin extends PluginInterface let deathKey = sc.get(gameManager.config.client.skills.animations, message.k + '_death', 'default_death'); let currentScene = gameManager.activeRoomEvents.getActiveScene(); let skeletonSprite = currentScene.physics.add.sprite(message.x, message.y, deathKey); - skeletonSprite.setDepth(200000); + skeletonSprite.setDepth(10500); skeletonSprite.anims.play(deathKey, true).on('animationcomplete', () => { skeletonSprite.destroy(); }); diff --git a/lib/objects/constants.js b/lib/objects/constants.js index acad20a2f..5d1e42215 100644 --- a/lib/objects/constants.js +++ b/lib/objects/constants.js @@ -21,6 +21,14 @@ module.exports.ObjectsConst = { NAMESPACE: 'objects' } }, + EVENT_PREFIX: { + BASE: 'bo', + ANIMATION: 'ao', + DROP: 'dep', + ENEMY: 'eo', + NPC: 'npc', + TRADER: 'tnpc' + }, SNIPPETS: { PREFIX: snippetsPrefix, NPC_INVALID: snippetsPrefix+'npcInvalid', diff --git a/lib/objects/server/manager.js b/lib/objects/server/manager.js index 1949b9b3f..436b47ed7 100644 --- a/lib/objects/server/manager.js +++ b/lib/objects/server/manager.js @@ -13,6 +13,8 @@ class ObjectsManager { this.config = props.config; this.events = sc.get(props, 'events', false); + this.roomId = sc.get(props, 'roomId', false); + this.roomName = sc.get(props, 'roomName', false); if(!this.events){ Logger.error('EventsManager undefined in ObjectsManager.'); } diff --git a/lib/objects/server/object/type/animation-object.js b/lib/objects/server/object/type/animation-object.js index 08b6f4242..200bb364c 100644 --- a/lib/objects/server/object/type/animation-object.js +++ b/lib/objects/server/object/type/animation-object.js @@ -16,11 +16,10 @@ class AnimationObject extends BaseObject constructor(props) { super(props); - // object type: this.type = ObjectsConst.TYPE_ANIMATION; this.isAnimation = true; - this.eventsPrefix = 'ao'; - // assign extra public params: + this.eventsPrefix = this.uid+'.'+ObjectsConst.EVENT_PREFIX.ANIMATION; + // the clientParams are all public params broadcast to the client: this.clientParams = { type: this.type, enabled: true, diff --git a/lib/objects/server/object/type/base-object.js b/lib/objects/server/object/type/base-object.js index abf29dca0..fca152fad 100644 --- a/lib/objects/server/object/type/base-object.js +++ b/lib/objects/server/object/type/base-object.js @@ -2,9 +2,6 @@ * * Reldens - BaseObject * - * Every object created will have a position. - * Objects are just an internal platform definition, different from game items which are in a different module. - * */ const { ObjectsConst } = require('../../../constants'); @@ -16,7 +13,7 @@ class BaseObject extends InteractionArea constructor(props) { super(); - // then we will assign all the properties from the storage automatically as part of this object. + // assign all the properties from the storage automatically as part of this object: Object.assign(this, props); if(!this.events){ Logger.error('EventsManager undefined in BaseObject.'); @@ -28,17 +25,21 @@ class BaseObject extends InteractionArea Logger.error('Data Server undefined in BaseObject.'); } this.appendIndex = sc.get(props, 'tile_index', props.id); - this.objectIndex = props.layer_name + this.appendIndex; + this.objectIndex = props.layer_name + (this.appendIndex || '-idx-1'); // we will use the client_key has the object key: this.key = props.client_key; - this.uid = this.key + Date.now(); - this.eventsPrefix = 'bo'; - // defaults: + this.uid = this.key +'-'+ Date.now(); + this.eventsPrefix = this.uid+'.'+ObjectsConst.EVENT_PREFIX.BASE; this.setDefaultProperties(); this.mapClientParams(props); this.mapPrivateParams(props); } + eventUniqueKey(suffix) + { + return this.eventsPrefix+'.'+(new Date()).getTime()+(suffix ? '.'+suffix : ''); + } + setDefaultProperties() { this.runOnHit = false; diff --git a/lib/objects/server/object/type/drop-object.js b/lib/objects/server/object/type/drop-object.js index a69ce44fb..e00f06cdc 100644 --- a/lib/objects/server/object/type/drop-object.js +++ b/lib/objects/server/object/type/drop-object.js @@ -5,6 +5,7 @@ */ const { AnimationObject } = require('./animation-object'); +const { ObjectsConst } = require('../../../constants'); const { RewardsConst } = require('../../../../rewards/constants'); class DropObject extends AnimationObject @@ -14,7 +15,7 @@ class DropObject extends AnimationObject { super(props); this.type = RewardsConst.REWARDS_PICK_UP_ACT; - this.eventsPrefix = RewardsConst.DROP_EVENT_PREFIX; + this.eventsPrefix = this.uid+'.'+ObjectsConst.EVENT_PREFIX.DROP; this.listenMessages = true; this.clientParams.type = RewardsConst.REWARDS_PICK_UP_ACT; this.clientParams.isInteractive = true; diff --git a/lib/objects/server/object/type/enemy-object.js b/lib/objects/server/object/type/enemy-object.js index d36089cec..482c574b5 100644 --- a/lib/objects/server/object/type/enemy-object.js +++ b/lib/objects/server/object/type/enemy-object.js @@ -24,7 +24,7 @@ class EnemyObject extends NpcObject this.stats = Object.assign({}, configStats); this.statsBase = Object.assign({}, configStats); this.type = ObjectsConst.TYPE_ENEMY; - this.eventsPrefix = 'eo'; + this.eventsPrefix = this.uid+'.'+ObjectsConst.EVENT_PREFIX.ENEMY; // @NOTE: we could run different actions and enemies reactions based on the player action. // this.runOnAction = true; // run on hit will make the enemy aggressive when the player enter the in the enemy-object interactive area. @@ -62,7 +62,10 @@ class EnemyObject extends NpcObject this.setupDefaultAction(); this.respawnTime = false; this.respawnTimer = false; + this.respawnTimerInterval = false; + this.respawnStateTimer = false; this.respawnLayer = false; + this.postBroadPhaseListener = []; this.mapClientParams(props); this.mapPrivateParams(props); } @@ -76,11 +79,10 @@ class EnemyObject extends NpcObject this.events.onWithKey( this.getBattleEndEvent(), await this.onBattleEnd.bind(this), - this.getEventRemoveKey(), - this.getEventMasterKey() + this.eventUniqueKey('battleEnd'), + // @NOTE: objects use their uid as master key for the event listeners. + this.uid ); - let dataArr = this.events.listeners('reldens.battleEnded'); - this.battleEndListener = dataArr[dataArr.length -1]; } setupAggressiveBehavior() @@ -88,13 +90,24 @@ class EnemyObject extends NpcObject if(!this.isAggressive){ return; } - this.events.on('reldens.sceneRoomOnCreate', (room) => { - room.roomWorld.on('postBroadphase', (event) => { - if(0 === this.battle.inBattleWithPlayer.length){ - this.waitForPlayersToEnterRespawnArea(event, room); - } - }); - }); + this.events.onWithKey( + 'reldens.sceneRoomOnCreate', + this.attachAggressiveBehaviorEvent.bind(this), + this.eventUniqueKey('attachAggressiveBehavior'), + // @NOTE: objects use their uid as master key for the event listeners. + this.uid + ); + } + + attachAggressiveBehaviorEvent(room) + { + let newPostBroadPhaseListener = (event) => { + if(0 === this.battle.inBattleWithPlayer.length){ + this.waitForPlayersToEnterRespawnArea(event, room); + } + }; + this.postBroadPhaseListener.push(newPostBroadPhaseListener); + room.roomWorld.on('postBroadphase', newPostBroadPhaseListener); } waitForPlayersToEnterRespawnArea(event, room) @@ -152,6 +165,26 @@ class EnemyObject extends NpcObject async executePhysicalSkill(target, executedSkill) { + let targetBody = target.physicalBody || target.objectBody; + if(!targetBody){ + Logger.info('Target body is missing or do not have a body to be hit by a physical object.'); + return false; + } + if(!targetBody.world){ + Logger.error('Target body world is missing. Body ID: '+ targetBody.id); + return false; + } + let thisWorldKey = this.objectBody?.world?.worldKey; + let targetWorldKey = targetBody?.world?.worldKey; + let enemyObjectUid = this.uid; + if(thisWorldKey && targetWorldKey && thisWorldKey !== targetWorldKey){ + Logger.critical('Garbage enemy instance found.', { + enemyObjectUid, + thisWorldKey, + targetWorldKey + }); + return false; + } let messageData = Object.assign({skillKey: executedSkill.key}, executedSkill.owner.getPosition()); if(sc.isObjectFunction(executedSkill.owner, 'getSkillExtraData')){ let params = {skill: executedSkill, target}; @@ -170,15 +203,6 @@ class EnemyObject extends NpcObject if(animData){ executedSkill.animDir = sc.get(animData.animationData, 'dir', false); } - let targetBody = target.physicalBody || target.objectBody; - 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; - } targetBody.world.shootBullet(from, to, executedSkill); } @@ -193,17 +217,14 @@ class EnemyObject extends NpcObject if(!skillData){ return false; } - let skillInstance = new skillData['class']( - Object.assign( - { - owner: this, - ownerIdProperty: 'uid', - affectedProperty: this.defaultAffectedProperty, - events: this.events - }, - skillData['data'] - ) - ); + let skillOwnerData = Object.assign({ + owner: this, + ownerIdProperty: 'uid', + eventsPrefix: this.eventsPrefix, + affectedProperty: this.defaultAffectedProperty, + events: this.events + }, skillData['data']); + let skillInstance = new skillData['class'](skillOwnerData); this.actionsKeys.push(skillKey); this.actions[skillKey] = skillInstance; this.actionsTargets[skillKey] = skillTarget; @@ -212,17 +233,7 @@ class EnemyObject extends NpcObject getBattleEndEvent() { - return this.key+'.reldens.battleEnded'; - } - - getEventRemoveKey() - { - return this.key+'battleEnd'; - } - - getEventMasterKey() - { - return 'battleRoom'; + return this.eventUniqueKey()+'emittedBattleEnded'; } async respawn(room) @@ -240,19 +251,35 @@ class EnemyObject extends NpcObject restoreOnTimeOut(room) { + let respawnStartTime = Date.now(); + this.respawnTimerInterval = setInterval(() => { + const elapsedTime = Date.now() - respawnStartTime; + const remainingTime = Math.max(0, (this.respawnTime - elapsedTime) / 1000); + Logger.debug(`Respawn Object "${this.uid}" in: ${remainingTime.toFixed(2)} seconds`); + }, 1000); this.respawnTimer = setTimeout(async () => { + clearInterval(this.respawnTimerInterval); await this.restoreObject(room); - }, this.respawnTime*0.9); + }, this.respawnTime); } async restoreObject(room) { this.stats = Object.assign({}, this.initialStats); if(!this.objectBody.world){ - Logger.error('ObjectBody world is null on restoreObject method.', this.objectBody.id); + Logger.error('ObjectBody world is null on restoreObject method for object UID: "'+this.uid+'".'); return; } - this.objectBody.bodyState.inState = GameConst.STATUS.AVOID_INTERPOLATION; + let interpolationStatus = GameConst.STATUS.AVOID_INTERPOLATION; + this.objectBody.bodyState.inState = interpolationStatus; + if(interpolationStatus !== this.battle.targetObject.objectBody.bodyState.inState){ + Logger.warning('Battle target object state miss match, set it to avoid interpolation.'); + this.battle.targetObject.objectBody.bodyState.inState = interpolationStatus; + } + if(interpolationStatus !== room.objectsManager.roomObjects[this.objectIndex].objectBody.bodyState.inState){ + Logger.warning('Objects Manager room object state miss match, set it to avoid interpolation.'); + room.objectsManager.roomObjects[this.objectIndex].objectBody.bodyState.inState = interpolationStatus; + } let respawnArea = this.objectBody.world.respawnAreas[this.respawnLayer]; delete respawnArea.usedTiles[this.randomTileIndex]; let {randomTileIndex, tileData} = respawnArea.getRandomTile(this.objectIndex); @@ -261,14 +288,27 @@ class EnemyObject extends NpcObject Object.assign(this, tileData); let { x, y } = tileData; this.objectBody.position = [x, y]; - this.objectBody.originalCol = x; - this.objectBody.originalRow = y; this.objectBody.bodyState.x = x; this.objectBody.bodyState.y = y; + let {currentCol, currentRow} = this.objectBody.positionToTiles(x, y); + this.objectBody.originalCol = currentCol; + this.objectBody.originalRow = currentRow; await this.events.emit('reldens.restoreObjectAfter', {enemyObject: this, room}); - setTimeout(()=> { - this.objectBody.bodyState.inState = GameConst.STATUS.ACTIVE; - }, (this.respawnTime || 1000)*0.1); + let respawnTime = this.respawnTime || 1000; + Logger.debug('Respawn: '+this.uid+ ' - Time: '+respawnTime+' - Position x/y: '+x+' / '+y); + this.respawnStateTimer = setTimeout(()=> { + Logger.debug('Activated object after respawn: '+this.uid); + let activeStatus = GameConst.STATUS.ACTIVE; + this.objectBody.bodyState.inState = activeStatus; + if(activeStatus !== this.battle.targetObject.objectBody.bodyState.inState){ + Logger.warning('Battle target object state miss match, set it to active.'); + this.battle.targetObject.objectBody.bodyState.inState = activeStatus; + } + if(activeStatus !== room.objectsManager.roomObjects[this.objectIndex].objectBody.bodyState.inState){ + Logger.warning('Objects Manager room object state miss match, set it to active.'); + room.objectsManager.roomObjects[this.objectIndex].objectBody.bodyState.inState = activeStatus; + } + }, 1); } onHit(props) @@ -282,7 +322,6 @@ class EnemyObject extends NpcObject { let playerBody = sc.hasOwn(props.bodyA, 'playerId') ? props.bodyA : props.bodyB; if(!props.room || !playerBody){ - // this shouldn't happen :P Logger.error('Required properties room and playerBody not found.'); return; } @@ -299,6 +338,7 @@ class EnemyObject extends NpcObject getPosition() { + // @TODO - BETA - Check if we need to update and return this.x, this.y or these are just the initial position. return { x: this.state.x, y: this.state.y @@ -307,7 +347,7 @@ class EnemyObject extends NpcObject async onBattleEnd() { - Logger.info('Battle end method not implemented for EnemyObject.', this.key, this.title); + Logger.debug('BattleEnd method not implemented for EnemyObject.', this.uid, this.title); } } diff --git a/lib/objects/server/object/type/npc-object.js b/lib/objects/server/object/type/npc-object.js index 564b9d43f..e988825d3 100644 --- a/lib/objects/server/object/type/npc-object.js +++ b/lib/objects/server/object/type/npc-object.js @@ -19,7 +19,7 @@ class NpcObject extends AnimationObject this.type = ObjectsConst.TYPE_NPC; this.hasAnimation = true; this.collisionResponse = true; - this.eventsPrefix = 'npc'; + this.eventsPrefix = this.uid+'.'+ObjectsConst.EVENT_PREFIX.NPC; this.listenMessages = true; // interactive objects will react on click: this.clientParams.type = ObjectsConst.TYPE_NPC; diff --git a/lib/objects/server/object/type/trader-object.js b/lib/objects/server/object/type/trader-object.js index 375dcf2b8..cf2159696 100644 --- a/lib/objects/server/object/type/trader-object.js +++ b/lib/objects/server/object/type/trader-object.js @@ -24,7 +24,7 @@ class TraderObject extends NpcObject { super(props); this.type = ObjectsConst.TYPE_TRADER; - this.eventsPrefix = 'tnpc'; + this.eventsPrefix = this.uid+'.'+ObjectsConst.EVENT_PREFIX.TRADER; this.clientParams.type = ObjectsConst.TYPE_TRADER; this.sendInvalidOptionMessage = true; this.inventory = false; diff --git a/lib/objects/server/plugin.js b/lib/objects/server/plugin.js index bfb23c18d..121b100b6 100644 --- a/lib/objects/server/plugin.js +++ b/lib/objects/server/plugin.js @@ -13,6 +13,7 @@ class ObjectsPlugin extends PluginInterface setup(props) { + this.objectsClassTypeHandler = false; this.events = sc.get(props, 'events', false); if(!this.events){ ErrorManager.error('EventsManager undefined in RewardsPlugin.'); diff --git a/lib/prediction/client/prediction-world-creator.js b/lib/prediction/client/prediction-world-creator.js index 95f6b9a43..a69c9bf85 100644 --- a/lib/prediction/client/prediction-world-creator.js +++ b/lib/prediction/client/prediction-world-creator.js @@ -49,7 +49,6 @@ class PredictionWorldCreator predictionBody.updateBodyState = this.updateBodyStateOverride(predictionBody, currentPlayer); currentPlayer.predictionBody = predictionBody; scene.worldPredictionTimer = new WorldTimer({ - world: scene.worldPrediction, callbacks: [() => { if(!scene.worldPrediction){ Logger.error('Scene World not longer exists.', scene.roomWorld); @@ -58,7 +57,7 @@ class PredictionWorldCreator scene.worldPrediction.removeBodiesFromWorld(); }] }); - scene.worldPredictionTimer.startWorldSteps(); + scene.worldPredictionTimer.startWorldSteps(scene.worldPrediction); scene.collisionsManager = new CollisionsManager({roomWorld: scene.worldPrediction}); currentPlayer.pointsValidator = new WorldPointsValidator(mapJson.width, mapJson.height); } diff --git a/lib/respawn/server/plugin.js b/lib/respawn/server/plugin.js index fc2870088..2e5717c23 100644 --- a/lib/respawn/server/plugin.js +++ b/lib/respawn/server/plugin.js @@ -31,6 +31,7 @@ class RespawnPlugin extends PluginInterface listenEvents() { if(!this.events){ + Logger.critical('Undefined events on RespawnPlugin.'); return false; } this.events.on('reldens.parsingMapLayerBefore', async (eventData = {}) => { @@ -45,9 +46,7 @@ class RespawnPlugin extends PluginInterface } await this.createRoomRespawnArea(layer, world); }); - this.events.on('reldens.sceneRoomOnCreate', async (room) => { - return this.createRespawnObjectsInstances(room); - }); + this.events.on('reldens.sceneRoomOnCreate', this.createRespawnObjectsInstances.bind(this)); } createRespawnObjectsInstances(room) @@ -75,7 +74,7 @@ class RespawnPlugin extends PluginInterface continue; } // @TODO - BETA - Refactor and extract Colyseus into a driver. - room.state.bodies.set(objInstance.client_key, objInstance.state); + room.state.addBodyToState(objInstance.state, objInstance.client_key); } } } diff --git a/lib/respawn/server/room-respawn.js b/lib/respawn/server/room-respawn.js index cece58db3..8038398f8 100644 --- a/lib/respawn/server/room-respawn.js +++ b/lib/respawn/server/room-respawn.js @@ -115,6 +115,7 @@ class RoomRespawn objInstance.objectIndex = objectIndex; objInstance.randomTileIndex = randomTileIndex; this.instancesCreated[respawnArea.id].push(objInstance); + Logger.debug({respawnCreatedWorldObject: objInstance.uid}); } getObjectAssets(multipleObj) @@ -129,6 +130,10 @@ class RoomRespawn generateObjectIndex(respawnArea) { let newIndex = this.instancesCreated[respawnArea.id].length; + Logger.debug( + 'Generating new object index.', + {layerName: this.layer.name, newIndex, respawnAreaId: respawnArea.id} + ); return this.layer.name + '-' + respawnArea.id + '-' + newIndex; } diff --git a/lib/rewards/constants.js b/lib/rewards/constants.js index d59ae1d30..02d3411f0 100644 --- a/lib/rewards/constants.js +++ b/lib/rewards/constants.js @@ -13,7 +13,6 @@ module.exports.RewardsConst = { REWARDS_PARAMS: 'rp', REWARDS_PATH: '/assets/custom/sprites/', REWARDS_PICK_UP_ACT: 'rpu', - DROP_EVENT_PREFIX: 'dep', REMOVE_DROP: 'rd', SPLIT_EXPERIENCE: { ALL: 0, diff --git a/lib/rewards/server/plugin.js b/lib/rewards/server/plugin.js index 0611f366a..0ea773b3e 100644 --- a/lib/rewards/server/plugin.js +++ b/lib/rewards/server/plugin.js @@ -10,7 +10,7 @@ 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'); +const { Logger, sc } = require('@reldens/utils'); class RewardsPlugin extends PluginInterface { @@ -18,33 +18,56 @@ class RewardsPlugin extends PluginInterface setup(props) { this.events = sc.get(props, 'events', false); + this.listenEvents(); + } + + listenEvents() + { if(!this.events){ - ErrorManager.error('EventsManager undefined in RewardsPlugin.'); + Logger.critical('EventsManager undefined in RewardsPlugin.'); + return false; } - 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 - ); - }); + this.events.on('reldens.featuresManagerLoadFeaturesAfter', this.appendPluginClasses.bind(this)); + this.events.on('reldens.afterRunAdditionalRespawnSetup', this.enrichObjectWithRewards.bind(this)); + this.events.on('reldens.battleEnded', this.battleEndedGiveRewards.bind(this)); + this.events.on('reldens.sceneRoomOnCreate', this.attachGiveRewardsEvent.bind(this)); + this.events.on('reldens.roomsMessageActionsGlobal', this.attachRewardMessageActions.bind(this)); + } + + async attachGiveRewardsEvent(roomScene) + { + this.events.onWithKey( + 'reldens.afterGiveRewards', + this.processRewardsDrops.bind(this, roomScene), + roomScene.roomName+'-'+roomScene.roomId+'-afterGiveRewards-'+sc.getTime(), + roomScene.roomName+'-'+roomScene.roomId + ); + } + + async processRewardsDrops(roomScene, rewardEventData) + { + await RewardsDropsProcessor.processRewardsDrops(roomScene, rewardEventData); + } + + attachRewardMessageActions(roomMessageActions) + { + roomMessageActions.rewards = new RewardMessageActions(this.targetDeterminer); + } + + async appendPluginClasses(event) + { + this.rewardsSubscriber = new RewardsSubscriber(event); + this.targetDeterminer = new TargetDeterminer(event?.featuresManager?.featuresList?.teams.package); + } + + async enrichObjectWithRewards(event) + { + await ObjectSubscriber.enrichWithRewards(event.objInstance); + } + + async battleEndedGiveRewards(event) + { + await this.rewardsSubscriber.giveRewards(event.playerSchema, event.pve?.targetObject, this.events); } } diff --git a/lib/rewards/server/subscribers/object-subscriber.js b/lib/rewards/server/subscribers/object-subscriber.js index 14c3299ce..671212536 100644 --- a/lib/rewards/server/subscribers/object-subscriber.js +++ b/lib/rewards/server/subscribers/object-subscriber.js @@ -11,6 +11,9 @@ class ObjectSubscriber static async enrichWithRewards(objectInstance) { + if(!objectInstance){ + return; + } objectInstance['rewards'] = RewardsMapper.fromModels( await objectInstance.dataServer.getEntity('rewards').loadByWithRelations( 'object_id', diff --git a/lib/rewards/server/subscribers/rewards-subscriber.js b/lib/rewards/server/subscribers/rewards-subscriber.js index cc126a00e..5b55a8837 100644 --- a/lib/rewards/server/subscribers/rewards-subscriber.js +++ b/lib/rewards/server/subscribers/rewards-subscriber.js @@ -23,6 +23,9 @@ class RewardsSubscriber async giveRewards(playerSchema, targetObject, eventsManager) { + if(!targetObject){ + return; + } let eventDrop = {playerSchema, targetObject, continueEvent: true}; await eventsManager.emit('reldens.beforeGiveRewards', eventDrop); if(!eventDrop.continueEvent){ diff --git a/lib/rewards/server/world-drop-handler.js b/lib/rewards/server/world-drop-handler.js index 99150d0c6..61cc35d41 100644 --- a/lib/rewards/server/world-drop-handler.js +++ b/lib/rewards/server/world-drop-handler.js @@ -45,7 +45,8 @@ class WorldDropHandler continue; } roomScene.addObjectStateSceneData(rewardObjectBody); - this.setRewardTimeDisappear(rewardObjectBody, roomScene); + // @TODO - BETA - Clear timeout (possible bug if the timeout is not cleared). + newReward.autoDestroyTimeout = this.setRewardTimeDisappear(rewardObjectBody, roomScene); newReward['objectBody'] = rewardObjectBody; rewards.push(newReward); } @@ -151,7 +152,7 @@ class WorldDropHandler static setRewardTimeDisappear(dropObject, roomScene) { - setTimeout(() => { + return setTimeout(() => { if(!roomScene.objectsManager.getObjectData(dropObject.objectIndex)){ return false; } diff --git a/lib/rooms/server/game.js b/lib/rooms/server/game.js index ad5213fd5..85b6fd515 100644 --- a/lib/rooms/server/game.js +++ b/lib/rooms/server/game.js @@ -52,7 +52,7 @@ class RoomGame extends RoomLogin message.formData.user_id = client.auth.id; let result = await this.loginManager.createNewPlayer(message.formData); result.act = GameConst.CREATE_PLAYER_RESULT; - client.send('*', result); + return client.send('*', result); } } diff --git a/lib/rooms/server/login.js b/lib/rooms/server/login.js index 15a02430b..3dc48ea00 100644 --- a/lib/rooms/server/login.js +++ b/lib/rooms/server/login.js @@ -28,9 +28,7 @@ class RoomLogin extends Room this.validateRoomData = false; options.roomsManager.createdInstances[this.roomId] = this; this.events.emitSync('reldens.roomLoginOnCreate', {roomLogin: this, options}); - this.onMessage('*', async (client, message) => { - await this.handleReceivedMessage(client, message); - }); + this.onMessage('*', this.handleReceivedMessage.bind(this)); } async handleReceivedMessage(client, message) @@ -86,7 +84,8 @@ class RoomLogin extends Room onDispose() { - Logger.info('ON-DISPOSE Room: ' + this.roomName); + this.events.emitSync('reldens.onRoomDispose', {roomName: this.roomName, roomId: this.roomId}); + Logger.info('ON-DISPOSE Room: '+this.roomName); } } diff --git a/lib/rooms/server/manager.js b/lib/rooms/server/manager.js index c5a1f2bba..cd6eef11f 100644 --- a/lib/rooms/server/manager.js +++ b/lib/rooms/server/manager.js @@ -37,7 +37,7 @@ class RoomsManager if(!this.defineExtraRooms.length){ Logger.info('None extra rooms to be defined.'); } - // dispatch event to get the global message actions (that will be listen by every room): + // dispatch event to get the global message actions (that will be listened by every room): let globalMessageActions = {}; await this.events.emit('reldens.roomsMessageActionsGlobal', globalMessageActions); // lobby room: diff --git a/lib/rooms/server/plugin.js b/lib/rooms/server/plugin.js index f1b6d2ad0..648c5b26f 100644 --- a/lib/rooms/server/plugin.js +++ b/lib/rooms/server/plugin.js @@ -16,19 +16,40 @@ class RoomsPlugin extends PluginInterface if(!this.events){ Logger.error('EventsManager undefined in RoomsPlugin.'); } - this.events.on('reldens.beforeSuperInitialGameData', async (superInitialGameData, roomGame) => { - await this.onBeforeSuperInitialGameData(superInitialGameData, roomGame); - }); + this.listenEvents(); } - async onBeforeSuperInitialGameData(superInitialGameData, roomGame) + listenEvents() + { + if(!this.events){ + return false; + } + this.events.on('reldens.serverBeforeListen', this.attachRoomsManager.bind(this)); + this.events.on('reldens.beforeSuperInitialGameData', this.attachRoomSelectionAndInstancesRemoval.bind(this)); + this.events.on('reldens.onRoomDispose', this.removeRoomCreatedInstanceFromManager.bind(this)); + } + + attachRoomsManager(event) + { + this.roomsManager = event?.serverManager?.roomsManager; + } + + async attachRoomSelectionAndInstancesRemoval(superInitialGameData, roomGame) { let config = roomGame.config.get('client/rooms/selection'); + if(!config){ + Logger.critical('Missing configuration on RoomsPlugin.'); + return false; + } + if(!this.roomsManager){ + Logger.critical('Missing RoomsManager on RoomsPlugin.'); + return false; + } if(config.allowOnRegistration || config.allowOnLogin){ - let isRegistration = (!superInitialGameData.players || superInitialGameData.players.length === 0); + let isRegistration = (!superInitialGameData.players || 0 === superInitialGameData.players.length); let configuredRooms = (isRegistration ? config.registrationAvailableRooms : config.loginAvailableRooms); - let currentRooms = roomGame.loginManager.roomsManager.loadedRoomsByName; - let availableRooms = configuredRooms === '*' ? Object.keys(currentRooms) : (await this.filterValidRooms( + let currentRooms = this.roomsManager.loadedRoomsByName; + let availableRooms = '*' === configuredRooms ? Object.keys(currentRooms) : (await this.filterValidRooms( configuredRooms.split(','), currentRooms )); @@ -39,6 +60,11 @@ class RoomsPlugin extends PluginInterface } } + removeRoomCreatedInstanceFromManager(roomData) + { + delete this.roomsManager.createdInstances[roomData.roomId]; + } + async filterValidRooms(configuredRooms, createdRooms) { let validRooms = []; diff --git a/lib/rooms/server/scene.js b/lib/rooms/server/scene.js index b28e998ea..7af660600 100644 --- a/lib/rooms/server/scene.js +++ b/lib/rooms/server/scene.js @@ -21,18 +21,26 @@ class RoomScene extends RoomLogin async onCreate(options) { + super.onCreate(options); + this.autoDispose = sc.get(options.roomData, 'autoDispose', true); + this.roomWorld = {}; this.activePlayers = {}; this.messageActions = {}; this.movementInterval = {}; - super.onCreate(options); + this.worldTimerCallback = false; this.roomType = RoomsConst.ROOM_TYPE_SCENE; - // override super prop: this.validateRoomData = true; - Logger.info('Created RoomScene: '+this.roomName+' - ID: '+this.roomId); + Logger.info( + 'Created RoomScene: '+this.roomName+' - ID: '+this.roomId + +' - With AutoDispose Enabled: '+(this.autoDispose ? 'Yes' : 'No') + ); this.sceneId = this.roomId; // @NOTE: we create an instance of the objects manager for each room-scene, this is on purpose so all the // related object instances will be removed when the room is disposed. - let objectsManagerConfig = Object.assign({events: this.events}, options); + let objectsManagerConfig = Object.assign( + {events: this.events, roomName: this.roomName, roomId: this.roomId}, + options + ); this.objectsManager = new ObjectsManager(objectsManagerConfig); await this.objectsManager.loadObjectsByRoomId(options.roomData.roomId); if(this.objectsManager.roomObjectsData){ @@ -42,7 +50,6 @@ class RoomScene extends RoomLogin if(this.objectsManager.listenMessages){ Object.assign(this.messageActions, this.objectsManager.listenMessagesObjects); } - // world data: this.lastCallTime = Date.now() / 1000; this.paused = false; this.customData = options.roomData.customData || {}; @@ -163,6 +170,7 @@ class RoomScene extends RoomLogin if(updatedPlayer){ currentPlayer.syncPlayer(updatedPlayer); } + // @TODO - BETA - Remove this callbacks and provide a player container that will executed the required methods. currentPlayer.persistData = async (params) => { await this.events.emit('reldens.playerPersistDataBefore', client, userModel, currentPlayer, params, this); await this.savePlayedTime(currentPlayer); @@ -213,8 +221,8 @@ class RoomScene extends RoomLogin async handleReceivedMessage(client, messageData) { if(!messageData){ - Logger.error(['Empty message data:', messageData]); - return false; + Logger.error('Empty message data on room "'+this.roomName+'" ID "'+this.roomId+'".', messageData); + return; } // only process the message if the player exists and has a body: let playerSchema = this.playerBySessionIdFromState(client.sessionId); @@ -331,6 +339,7 @@ class RoomScene extends RoomLogin } clearInterval(this.movementInterval[i]); delete this.movementInterval[i]; + Logger.debug('Cleared movement interval: '+i); } } @@ -367,20 +376,26 @@ class RoomScene extends RoomLogin this.pointsValidator = new WorldPointsValidator(this.roomWorld.worldWidth, this.roomWorld.worldHeight); this.roomWorld.createLimits(); await this.roomWorld.createWorldContent(roomData); - // start world movement from the config or with the default value: + this.initializeWorldTimer(); + Logger.info('World created in Room: ' + this.roomName); + } + + initializeWorldTimer() + { + this.worldTimerCallback = () => { + if(!this.roomWorld){ + Logger.error('Room World not longer exists.', this.roomWorld); + return; + } + this.roomWorld.removeBodiesFromWorld(); + }; this.worldTimer = new WorldTimer({ - clockInstance: this.clock, + // @TODO - BETA - Create a single class/point for setIntervals or timeOuts instances creation. + // clockInstance: this.clock, world: this.roomWorld, - callbacks: [() => { - if(!this.roomWorld){ - Logger.error('Room World not longer exists.', this.roomWorld); - return; - } - this.roomWorld.removeBodiesFromWorld(); - }] + callbacks: [this.worldTimerCallback] }); - this.worldTimer.startWorldSteps(); - Logger.info('World created in Room: ' + this.roomName); + this.worldTimer.startWorldSteps(this.roomWorld); } createWorldInstance(data) @@ -478,11 +493,37 @@ class RoomScene extends RoomLogin removeAllPlayerReferences(playerSchema, sessionId) { + this.events.offByMasterKey(playerSchema.eventsPrefix); let bodyToRemove = playerSchema.physicalBody; if(bodyToRemove){ this.roomWorld.removeBody(bodyToRemove); } - this.events.offByMasterKey(playerSchema.eventsPrefix + playerSchema.player_id); + playerSchema.physicalBody = null; + let itemsList = playerSchema?.inventory?.manager?.items || {}; + let itemsKeys = Object.keys(itemsList); + if(0 < itemsKeys.length){ + for(let i of itemsKeys){ + let item = itemsList[i]; + if(item.useTimer){ + clearTimeout(item.useTimer); + } + if(item.execTimer){ + clearTimeout(item.execTimer); + } + } + } + this.clearEntityActions(playerSchema); + // these contain references to the room: + delete playerSchema.skillsServer?.client; + delete playerSchema.inventory?.client; + playerSchema.executePhysicalSkill = null; + playerSchema.persistData = null; + playerSchema.skillsServer = null; + playerSchema.inventory = null; + let playerDeathTimer = playerSchema.getPrivate('playerDeathTimer'); + if(playerDeathTimer){ + clearTimeout(playerDeathTimer); + } this.state.removePlayer(sessionId); } @@ -584,30 +625,151 @@ class RoomScene extends RoomLogin onDispose() { - Logger.info('ON-DISPOSE Room: ' + this.roomName); + Logger.info('ON-DISPOSE Room: '+this.roomName); + this.logEventsData('before'); this.clearMovementIntervals(); - clearInterval(this.roomWorld.worldTimer); + this.clearWorldTimers(); + this.cleanUpRoomWorld(); // @TODO - BETA - Replace this by a master key related to the room ID and just remove all the events related // to this room. + this.handleRespawnOnRoomDispose(); + this.handleObjectsManagerOnRoomDispose(); + delete this.objectsManager; + delete this.roomWorld; + this.roomWorld = {}; + this.events.offByMasterKey(this.roomName+'-'+this.roomId); + super.onDispose(); + this.logEventsData('after'); + } + + cleanUpRoomWorld() + { + this.roomWorld.removeBodies.push(this.objectBody); + this.roomWorld._listeners['postBroadphase'] = []; + this.roomWorld._listeners['preSolve'] = []; + this.roomWorld._listeners['beginContact'] = []; + this.roomWorld._listeners['endContact'] = []; + this.roomWorld.clear(); + } + + clearWorldTimers() + { + clearInterval(this.roomWorld.worldDateTimeInterval); + this.worldTimer.callbacks = []; + this.worldTimer.clockInstance ? this.worldTimer.worldTimer.clear() : clearInterval(this.worldTimer.worldTimer); + clearInterval(this.worldTimer.worldTimer); + delete this.worldTimerCallback; + this.worldTimerCallback = () => { + Logger.debug('World timer callback still executed for room "' + this.roomName + '".'); + }; + Logger.debug( + 'Cleared world timer and world date time intervals.', + { + worldTimerDestroyed: this.worldTimer.worldTimer._destroyed, + worldDateTimeDestroyed: this.roomWorld.worldDateTimeInterval._destroyed + } + ); + this.worldTimer.world = null; + this.worldTimer = null; + delete this.worldTimer; + } + + handleObjectsManagerOnRoomDispose() + { + if(!this.objectsManager.roomObjects){ + Logger.debug('None roomObjects defined for room: '+this.roomName); + return; + } + for(let i of Object.keys(this.objectsManager.roomObjects)){ + let objectInstance = this.objectsManager.roomObjects[i]; + objectInstance.postBroadPhaseListener = []; + this.clearEntityActions(objectInstance); + if(objectInstance.battle){ + clearTimeout(objectInstance.battle.battleTimer); + clearTimeout(objectInstance.battle.respawnStateTimer); + objectInstance.battle.targetObject = null; + } + clearTimeout(objectInstance.respawnTimer); + clearInterval(objectInstance.respawnTimerInterval); + clearTimeout(objectInstance.isCasting); + Logger.debug('Cleared timers for: ' + objectInstance.key + ' / ' + i); + delete this.objectsManager.roomObjects[i]; + } + } + + clearEntityActions(entityInstance) + { + let actions = entityInstance?.actions || {}; + let actionsKeys = Object.keys(actions); + if(0 < actionsKeys){ + for(let i of actionsKeys){ + if(actions[i].room){ + actions[i].room = null; + } + } + } + } + + handleRespawnOnRoomDispose() + { if(!this.roomWorld.respawnAreas){ - return true; + Logger.debug('None respawn areas defined for room: '+this.roomName); + return; + } + this.removeRespawnObjectsSubscribers(); + this.deleteRespawnObjectInstances(); + } + + deleteRespawnObjectInstances() + { + if(!this.roomWorld.respawnAreas){ + return; + } + for (let rI of Object.keys(this.roomWorld.respawnAreas)) { + let respawnInstancesCreated = this.roomWorld.respawnAreas[rI].instancesCreated; + for (let i of Object.keys(respawnInstancesCreated)) { + delete respawnInstancesCreated[i]; + Logger.debug('Deleted respawn instances created: ' + i); + } + delete this.roomWorld.respawnAreas[rI]; + Logger.debug('Deleted respawn area created: ' + rI); } - // 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)){ - let res = instC[i]; - for(let obj of res){ - if(!sc.hasOwn(obj, 'battleEndListener')){ - continue; - } - this.events.offWithKey(obj.key+'battleEnd', 'battleRoom'); + delete this.roomWorld.respawnAreas; + Logger.debug('Deleted respawn areas for room: ' + this.roomName); + } + + removeRespawnObjectsSubscribers() + { + if(!this.roomWorld.respawnAreas){ + return; + } + for (let rI of Object.keys(this.roomWorld.respawnAreas)) { + let respawnInstancesCreated = this.roomWorld.respawnAreas[rI].instancesCreated; + for (let i of Object.keys(respawnInstancesCreated)) { + let respawnInstance = respawnInstancesCreated[i]; + for (let respawnObject of respawnInstance) { + Logger.debug('Setting "battleEnd" listener off: ' + respawnObject.uid); + this.events.offByMasterKey(respawnObject.uid); } } } } + logEventsData(eventKey) + { + let endSubscribersCount = 0; + let keysData = {}; + for (let i of Object.keys(this.events._events)) { + endSubscribersCount += this.events._events[i].length; + keysData[i] = {keys: []}; + for (let event of this.events._events[i]) { + keysData[i].keys.push((event.fn.name || 'anonymous').replace('bound ', '')); + } + keysData[i].keys = keysData[i].keys.join(', '); + } + Logger.debug('Subscribers count '+eventKey+':', endSubscribersCount, keysData); + } + addObjectStateSceneData(object) { let sceneData = sc.toJson(this.state.sceneData); diff --git a/lib/rooms/server/state.js b/lib/rooms/server/state.js index 258359cc3..184656c36 100644 --- a/lib/rooms/server/state.js +++ b/lib/rooms/server/state.js @@ -62,6 +62,26 @@ class State extends Schema this.players.delete(id); } + fetchBody(id) + { + return this.bodies.get(id); + } + + addBodyToState(body, bodyId) + { + // @TODO - BETA - Refactor and extract Colyseus into a driver. + this.bodies.set(bodyId, body); + return this.bodies.get(bodyId); + } + + removeBody(id) + { + if(!this.fetchBody(id)){ + return false; + } + return this.bodies.delete(id); + } + } type('string')(State.prototype, 'sceneData'); diff --git a/lib/rooms/server/world-config.js b/lib/rooms/server/world-config.js index f8c9182c1..cd2de9895 100644 --- a/lib/rooms/server/world-config.js +++ b/lib/rooms/server/world-config.js @@ -35,12 +35,12 @@ class WorldConfig let applyGravity = sc.get(room.customData, 'applyGravity', globalConfig.applyGravity); let defaultsVariations = { useFixedWorldStep: null !== globalConfig.useFixedWorldStep ? globalConfig.useFixedWorldStep : !applyGravity, - timeStep: null !== globalConfig.timeStep ? globalConfig.timeStep : (applyGravity ? 0.012 : 0.04), - maxSubSteps: null !== globalConfig.maxSubSteps ? globalConfig.maxSubSteps : (applyGravity ? 5 : 1), - movementSpeed: null !== globalConfig.movementSpeed - ? globalConfig.movementSpeed : (applyGravity ? 200 : 180), + timeStep: applyGravity ? 0.012 : (null !== globalConfig.timeStep ? globalConfig.timeStep : 0.04), + maxSubSteps: applyGravity ? 2 : (null !== globalConfig.maxSubSteps ? globalConfig.maxSubSteps : 1), + movementSpeed: applyGravity ? 160 : (null !== globalConfig.movementSpeed ? globalConfig.movementSpeed : 180), allowPassWallsFromBelow: null !== globalConfig.allowPassWallsFromBelow - ? globalConfig.allowPassWallsFromBelow : false, + ? globalConfig.allowPassWallsFromBelow + : false, }; let worldConfig = { applyGravity, 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 index bb1c33b3b..a53f07403 100644 --- 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 @@ -13,7 +13,7 @@ class EndPlayerHitChangePointTeamHandler { let teamId = playerSchema.currentTeam; if(!teamId){ - Logger.info('Player "'+playerSchema?.playerName+'" (ID "'+playerSchema?.player_id+'"), team not saved.'); + Logger.debug('Player "'+playerSchema?.playerName+'" (ID "'+playerSchema?.player_id+'"), team not saved.'); return false; } teamsPlugin.teamChangingRoomPlayers[playerSchema.player_id] = { diff --git a/lib/users/client/player-engine.js b/lib/users/client/player-engine.js index 644db1f7a..6b8e254ac 100644 --- a/lib/users/client/player-engine.js +++ b/lib/users/client/player-engine.js @@ -35,6 +35,7 @@ class PlayerEngine this.pointsValidator = false; // @TODO - BETA - Set all the configs in a single config property. this.animationBasedOnPress = this.config.get('client/players/animations/basedOnPress'); + // @TODO - BETA - Make size configurations depend on class-paths assets if present. this.topOff = this.gameManager.config.get('client/players/size/topOffset'); this.leftOff = this.gameManager.config.get('client/players/size/leftOffset'); this.collideWorldBounds = this.gameManager.config.get('client/players/animations/collideWorldBounds'); @@ -73,6 +74,7 @@ class PlayerEngine } let {x, y, dir, playerName, avatarKey, playedTime, player_id} = addPlayerData; let mappedAvatarKey = this.gameManager.mappedAvatars[avatarKey]; + Logger.debug({mappedAvatarKey, avatarKey, mappedAvatars: this.gameManager.mappedAvatars}); this.players[id] = this.scene.physics.add.sprite(x, (y - this.topOff), mappedAvatarKey); this.players[id].playerName = playerName; this.players[id].playedTime = playedTime; @@ -151,32 +153,58 @@ class PlayerEngine this.scene.interpolatePlayersPosition[playerId] = player.state; return; } - this.playPlayerAnimation(playerSprite, player.state); - this.stopPlayerAnimation(playerSprite, player.state); - this.updateSpritePosition(playerSprite, (player.state.x - this.leftOff), (player.state.y - this.topOff)); - this.updatePlayerState(playerSprite, player.state, playerId); + this.processPlayerPositionAnimationUpdate( + playerSprite, + player.state, + playerId, + player.state.x - this.leftOff, + player.state.y - this.topOff + ); + } + + processPlayerPositionAnimationUpdate(playerSprite, playerState, playerId, newX = 0, newY = 0) + { + Logger.debug('Process player position animation update.', {playerSprite, playerState, playerId, newX, newY}); + if(!playerSprite){ + Logger.error('Missing player sprite to process animation update.', playerSprite, playerState, playerId); + return; + } + if(!playerState){ + Logger.error('Missing player state to process animation update.', playerSprite, playerState, playerId); + return; + } + if(!playerId){ + Logger.error('Missing player ID to process animation update.', playerSprite, playerState, playerId); + return; + } + this.playPlayerAnimation(playerSprite, playerState); + this.stopPlayerAnimation(playerSprite, playerState); + this.updateSpritePosition(playerSprite, newX, newY); + this.updatePlayerState(playerSprite, playerState, playerId); } updatePlayerState(playerSprite, playerState, playerId) { // @NOTE: depth has to be set dynamically, this way the player will be above or below other objects. - let playerNewDepth = this.updateSpriteDepth(playerSprite); + let playerNewDepth = playerSprite.y + playerSprite.body.height; + if(playerSprite.depth === playerNewDepth){ + Logger.debug('Player state has not changed.', playerState, playerSprite); + return false; + } + playerSprite.setDepth(playerNewDepth); this.events.emitSync('reldens.runPlayerAnimation', this, playerId, playerState, playerSprite); this.updateNamePosition(playerSprite); this.moveAttachedSprites(playerSprite, playerNewDepth); } - updateSpriteDepth(sprite) - { - let playerNewDepth = sprite.y + sprite.body.height; - sprite.setDepth(playerNewDepth); - return playerNewDepth; - } - updateSpritePosition(sprite, newX, newY) { - sprite.x = newX; - sprite.y = newY; + if(sprite.x !== newX){ + sprite.x = newX; + } + if(sprite.y !== newY){ + sprite.y = newY; + } } updateNamePosition(playerSprite) @@ -202,10 +230,15 @@ class PlayerEngine } for(let i of playersKeys){ let sprite = playerSprite.moveSprites[i]; + if(sprite.x === playerSprite.x && sprite.y === playerSprite.y){ + continue; + } sprite.x = playerSprite.x; sprite.y = playerSprite.y; // by default moving sprites will be always below the player: - sprite.setDepth((sc.get(sprite, 'depthByPlayer', '') === 'above' ? playerNewDepth+1 : playerNewDepth-0.1)); + let newSpriteDepth = playerNewDepth + (sc.get(sprite, 'depthByPlayer', '') === 'above' ? 1 : -0.1); + Logger.debug('Sprite "'+i+'" new depth: '+newSpriteDepth+'.', sprite); + sprite.setDepth(newSpriteDepth); } } @@ -214,7 +247,13 @@ class PlayerEngine // @NOTE: player speed is defined by the server. let activeAvatarKey = this.gameManager.mappedAvatars[playerSprite.avatarKey]; if(this.animationBasedOnPress){ - playerSprite.anims.play(activeAvatarKey+'_'+playerState.dir, true); + let directionKey = activeAvatarKey+'_'+playerState.dir; + if(playerState.x === playerSprite.x && playerState.y === playerSprite.y){ + Logger.debug('Player has not changed, skipped animation "'+directionKey+'".'); + return false; + } + Logger.debug('Animation played based on press active.', activeAvatarKey); + playerSprite.anims.play(directionKey, true); return; } if(playerState.x !== playerSprite.x){ diff --git a/lib/users/server/entities/users-entity.js b/lib/users/server/entities/users-entity.js index 0fe0cac63..ef81946e6 100644 --- a/lib/users/server/entities/users-entity.js +++ b/lib/users/server/entities/users-entity.js @@ -44,20 +44,22 @@ class UsersEntity extends EntityProperties } }; - let listPropertiesKeys = Object.keys(properties); - let editPropertiesKeys = sc.removeFromArray(listPropertiesKeys, [ + let showProperties = Object.keys(properties); + let listProperties = [...showProperties]; + let editProperties = sc.removeFromArray([...listProperties], [ 'id', 'created_at', 'updated_at' ]); - listPropertiesKeys.splice(listPropertiesKeys.indexOf('password'), 1); + showProperties.splice(listProperties.indexOf('password'), 1); + listProperties.splice(listProperties.indexOf('password'), 1); return Object.assign({ - listProperties: listPropertiesKeys, - showProperties: Object.keys(properties), - filterProperties: listPropertiesKeys, - editProperties: editPropertiesKeys, + listProperties, + showProperties, + filterProperties: listProperties, + editProperties, properties }, extraProps); } diff --git a/lib/users/server/player.js b/lib/users/server/player.js index 67cc6ba30..589f4c827 100644 --- a/lib/users/server/player.js +++ b/lib/users/server/player.js @@ -63,6 +63,11 @@ class Player extends Schema return this.customData[key] = data; } + eventUniqueKey() + { + return this.eventsPrefix+'.'+(new Date()).getTime(); + } + getPosition() { return { diff --git a/lib/users/server/plugin.js b/lib/users/server/plugin.js index 047f8f8db..14d4f4d11 100644 --- a/lib/users/server/plugin.js +++ b/lib/users/server/plugin.js @@ -7,9 +7,9 @@ const { PluginInterface } = require('../../features/plugin-interface'); const { UsersConst } = require('../constants'); const { ObjectsConst } = require('../../objects/constants'); -const { Logger, sc } = require('@reldens/utils'); -const { SkillsEvents } = require('@reldens/skills'); const { ActionsConst } = require('../../actions/constants'); +const { SkillsEvents } = require('@reldens/skills'); +const { Logger, sc } = require('@reldens/utils'); class UsersPlugin extends PluginInterface { @@ -54,41 +54,57 @@ class UsersPlugin extends PluginInterface async activateLifeBar(configProcessor) { + if(!this.events){ + return false; + } if(!this.lifeBarConfig){ this.lifeBarConfig = configProcessor.get('client/ui/lifeBar'); } if(!this.lifeProp){ this.lifeProp = configProcessor.get('client/actions/skills/affectedProperty'); } - this.events.on('reldens.createPlayerStatsAfter', async (client, userModel, playerSchema, roomScene) => { - await this.updatePlayersLifebar(roomScene, client, playerSchema); - await this.updateEnemiesLifebar(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) => { + this.events.on('reldens.createPlayerStatsAfter', this.updateLifeBars.bind(this)); + this.events.on('reldens.savePlayerStatsUpdateClient', this.updateClientsWithPlayerStats.bind(this)); + this.events.on('reldens.runBattlePveAfter', this.sendLifeBarUpdate.bind(this)); + this.events.on( + 'reldens.actionsPrepareEventsListeners', + this.addEventListenerOnSkillAttackApplyDamage.bind(this) + ); + this.events.on('reldens.restoreObjectAfter', this.broadcastObjectUpdateAfterRestore.bind(this)); + } + + async addEventListenerOnSkillAttackApplyDamage(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')){ await this.updatePlayersLifebar(client.room, client, target); return; } - this.broadcastObjectUpdate(client, target); - }, 'skillAttackApplyDamageLifebar', classPath.owner[classPath.ownerIdProperty]); - }); - this.events.on('reldens.restoreObjectAfter', (event) => { - this.broadcastObjectUpdate(event.room, event.enemyObject); - }); + await this.broadcastObjectUpdate(client, target); + }, + classPath.getOwnerUniqueEventKey('skillAttackApplyDamageLifebar'), + classPath.getOwnerEventKey() + ); + } + + async broadcastObjectUpdateAfterRestore(event) + { + await this.broadcastObjectUpdate(event.room, event.enemyObject); + } + + async updateLifeBars(client, userModel, playerSchema, roomScene) + { + await this.updatePlayersLifebar(roomScene, client, playerSchema); + await this.updateEnemiesLifebar(roomScene); } - broadcastObjectUpdate(room, enemyObject) + async broadcastObjectUpdate(room, enemyObject) { - room.broadcast('*', { + return room.broadcast('*', { act: UsersConst.ACTION_LIFEBAR_UPDATE, [ActionsConst.DATA_OWNER_TYPE]: ActionsConst.DATA_TYPE_VALUE_OBJECT, [ActionsConst.DATA_OWNER_KEY]: enemyObject.broadcastKey, @@ -97,7 +113,7 @@ class UsersPlugin extends PluginInterface }); } - sendLifeBarUpdate(event) + async sendLifeBarUpdate(event) { if(!this.lifeBarConfig.showEnemies && !this.lifeBarConfig.showOnClick){ return false; @@ -106,7 +122,7 @@ class UsersPlugin extends PluginInterface if(!target.stats[this.lifeProp]){ return false; } - this.broadcastObjectUpdate(roomScene, target) + await this.broadcastObjectUpdate(roomScene, target) } async updatePlayersLifebar(roomScene, client, playerSchema) @@ -114,7 +130,7 @@ class UsersPlugin extends PluginInterface if(this.lifeBarConfig.showAllPlayers || this.lifeBarConfig.showOnClick){ return await this.updateAllPlayersLifeBars(roomScene); } - return await this.onSavePlayerStatsUpdateClient(client, playerSchema, roomScene); + return await this.updateClientsWithPlayerStats(client, playerSchema, roomScene); } async updateEnemiesLifebar(roomScene) @@ -129,8 +145,8 @@ class UsersPlugin extends PluginInterface } let updateData = { act: UsersConst.ACTION_LIFEBAR_UPDATE, - oT: ActionsConst.DATA_TYPE_VALUE_OBJECT, - oK: obj.broadcastKey, + [ActionsConst.DATA_OWNER_TYPE]: ActionsConst.DATA_TYPE_VALUE_OBJECT, + [ActionsConst.DATA_OWNER_KEY]: obj.broadcastKey, newValue: obj.stats[this.lifeProp], totalValue: obj.initialStats[this.lifeProp] }; @@ -147,8 +163,8 @@ class UsersPlugin extends PluginInterface let player = roomScene.playerBySessionIdFromState(i); let updateData = { act: UsersConst.ACTION_LIFEBAR_UPDATE, - oT: ActionsConst.DATA_TYPE_VALUE_PLAYER, - oK: player.sessionId, + [ActionsConst.DATA_OWNER_TYPE]: ActionsConst.DATA_TYPE_VALUE_PLAYER, + [ActionsConst.DATA_OWNER_KEY]: player.sessionId, newValue: player.stats[this.lifeProp], totalValue: player.statsBase[this.lifeProp] }; @@ -184,6 +200,7 @@ class UsersPlugin extends PluginInterface let playerSpeed = Number(currentPlayer.stats?.speed || 0); if(usePlayerSpeedProperty && 0 < playerSpeed){ currentPlayer.physicalBody.movementSpeed = playerSpeed; + Logger.debug('Use player speed: '+playerSpeed); } this.events.emit('reldens.createPlayerStatsAfter', client, userModel, currentPlayer, roomScene); } @@ -204,7 +221,7 @@ class UsersPlugin extends PluginInterface return {stats, statsBase}; } - async onSavePlayerStatsUpdateClient(client, playerSchema, roomScene) + async updateClientsWithPlayerStats(client, playerSchema, roomScene) { if( client.sessionId !== playerSchema.sessionId @@ -213,11 +230,10 @@ class UsersPlugin extends PluginInterface ){ return false; } - // @TODO - BETA - Replace "oT", "oK" by constants. let updateData = { act: UsersConst.ACTION_LIFEBAR_UPDATE, - oT: 'p', - oK: playerSchema.sessionId, + [ActionsConst.DATA_OWNER_TYPE]: ActionsConst.DATA_TYPE_VALUE_PLAYER, + [ActionsConst.DATA_OWNER_KEY]: playerSchema.sessionId, newValue: playerSchema.stats[this.lifeProp], totalValue: playerSchema.statsBase[this.lifeProp] }; diff --git a/lib/world/client/debug-world-creator.js b/lib/world/client/debug-world-creator.js index dd1f3117d..5a781e065 100644 --- a/lib/world/client/debug-world-creator.js +++ b/lib/world/client/debug-world-creator.js @@ -30,7 +30,6 @@ class DebugWorldCreator scene.debugWorld.createLimits(); await scene.debugWorld.createWorldContent({}); scene.debugWorldTimer = new WorldTimer({ - world: scene.debugWorld, callbacks: [() => { if(!scene.debugWorld){ Logger.error('Scene World not longer exists.', scene.roomWorld); @@ -39,7 +38,7 @@ class DebugWorldCreator scene.debugWorld.removeBodiesFromWorld(); }] }); - scene.debugWorldTimer.startWorldSteps(); + scene.debugWorldTimer.startWorldSteps(scene.debugWorld); scene.debugWorldRenderer = new Renderer(scene); } diff --git a/lib/world/client/plugin.js b/lib/world/client/plugin.js index 8e5e2b450..d7ca7b960 100644 --- a/lib/world/client/plugin.js +++ b/lib/world/client/plugin.js @@ -15,9 +15,9 @@ class WorldPlugin extends PluginInterface { this.gameManager = sc.get(props, 'gameManager', false); this.events = sc.get(props, 'events', false); - this.debugWorldCreator = new DebugWorldCreator(); + this.debugWorldCreator = false; if(this.validateProperties()){ - this.listenEvents(); + this.setupDebugMode(); } } @@ -34,11 +34,12 @@ class WorldPlugin extends PluginInterface return true; } - listenEvents() + setupDebugMode() { if(!this.gameManager.config.getWithoutLogs('client/world/debug/enabled', false)){ return false; } + this.debugWorldCreator = new DebugWorldCreator(); this.events.on('reldens.createEngineSceneDone', async (props) => { await this.debugWorldCreator.createSceneWorld(props.currentScene); }); diff --git a/lib/world/server/collisions-manager.js b/lib/world/server/collisions-manager.js index 53b5b9f7d..e4643cf25 100644 --- a/lib/world/server/collisions-manager.js +++ b/lib/world/server/collisions-manager.js @@ -70,11 +70,8 @@ class CollisionsManager this.room.roomWorld.removeBulletsStateIds.indexOf(stateId), 1 ); - if(!this.room.state.bodies[stateId]){ - continue; - } // @TODO - BETA - Refactor and extract Colyseus into a driver. - this.room.state.bodies.delete(stateId); + this.room.state.removeBody(stateId); } } @@ -204,13 +201,14 @@ class CollisionsManager let isChangingScene = sc.get(playerBody, 'isChangingScene', false); if(isChangingScene){ // @NOTE: if the player is already changing scene do nothing. - Logger.info('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.playerBySessionIdFromState(playerBody.playerId); let changeData = {prev: playerSchema.state.scene, next: changePoint.changeScenePoint}; + Logger.debug('Player "'+playerBody.playerId+'" hit change point.', changeData); 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 diff --git a/lib/world/server/p2world.js b/lib/world/server/p2world.js index 09640abc0..38495a982 100644 --- a/lib/world/server/p2world.js +++ b/lib/world/server/p2world.js @@ -59,6 +59,8 @@ class P2world extends World this.totalBodiesCreated = 0; this.queueBodies = []; this.enablePathFinder(); + this.enableWorldDateTime(); + this.worldKey = sc.randomChars(16); } saveMapData(options) @@ -68,11 +70,25 @@ class P2world extends World this.mapJson = sc.get(this.config.server.maps, this.sceneTiledMapFile, false); } if(!this.mapJson){ - Logger.critical('Map "'+this.sceneTiledMapFile+'" not found in server maps.', this.config.server.maps); + Logger.critical( + 'Map "'+this.sceneTiledMapFile+'" not found in server maps.', + Object.keys(this.config.server.maps) + ); ErrorManager.error('Map "'+this.sceneTiledMapFile+'" not found in server maps.'); } } + enableWorldDateTime() + { + this.worldDateTime = new Date(); + this.worldDateTimeInterval = setInterval(() => { + this.worldDateTime = new Date(); + Logger.debug( + 'World "'+this.worldKey+'" time: '+this.worldDateTime.toISOString().slice(0, 19).replace('T', ' ') + ); + }, 1000); + } + enablePathFinder() { if(!this.usePathFinder){ @@ -383,12 +399,12 @@ class P2world extends World if(this.usePathFinder && pathFinder){ bodyObject.pathFinder = pathFinder; } - Logger.info('Created object for objectIndex: ' + objectIndex); // try to get object instance from project root: this.addBody(bodyObject); // set data on room object: roomObject.state = bodyObject.bodyState; roomObject.objectBody = bodyObject; + Logger.info('Created object for objectIndex: '+objectIndex+' - At x/y: '+posX+' / '+posY+'.'); await this.events.emit('reldens.createdWorldObject', { p2world: this, roomObject, @@ -528,13 +544,17 @@ class P2world extends World return boxBody; } - fetchPlayerSpeed() + fetchPlayerSpeed(playerSpeed) { - let usePlayerSpeedConfig = this.config.get('server/players/physicsBody/usePlayerSpeedConfig'); - if(usePlayerSpeedConfig){ - return this.config.get('server/players/physicsBody/speed', this.movementSpeed); + let movementSpeed = this.movementSpeed; + if(this.config.get('server/players/physicsBody/usePlayerSpeedConfig')){ + let configSpeed = this.config.get('server/players/physicsBody/speed', playerSpeed); + if(0 < configSpeed){ + movementSpeed = configSpeed; + } } - return this.movementSpeed; + Logger.debug('Use movement speed: '+movementSpeed); + return movementSpeed; } shootBullet(fromPosition, toPosition, bulletObject) @@ -575,7 +595,7 @@ class P2world extends World let bodyStateId = bulletKey+'_bullet_'+bulletBody.id; bulletBody.bodyStateId = bodyStateId; // @TODO - BETA - Refactor and extract Colyseus into a driver. - bulletObject.room.state.bodies.set(bodyStateId, bulletBody.bodyState); + bulletObject.room.state.addBodyToState(bulletBody.bodyState, bodyStateId); // 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 56fbb82d3..f7cfbbf00 100644 --- a/lib/world/server/physical-body.js +++ b/lib/world/server/physical-body.js @@ -26,7 +26,7 @@ class PhysicalBody extends Body this.originalRow = false; this.jumpSpeed = sc.get(options, 'jumpSpeed', 540); this.jumpTimeMs = sc.get(options, 'jumpTimeMs', 180); - this.movementSpeed = sc.get(options, 'movementSpeed', 120); + this.movementSpeed = sc.get(options, 'movementSpeed', 180); } integrate(dt) diff --git a/lib/world/world-timer.js b/lib/world/world-timer.js index 03e0b768f..aa5a58aef 100644 --- a/lib/world/world-timer.js +++ b/lib/world/world-timer.js @@ -4,14 +4,13 @@ * */ -const { ErrorManager, sc } = require('@reldens/utils'); +const { Logger, sc } = require('@reldens/utils'); class WorldTimer { constructor(props) { - this.world = props.world; this.clockInstance = sc.get(props, 'clockInstance', false); this.callbacks = sc.get(props, 'callbacks', []); this.worldTimer = {}; @@ -20,47 +19,45 @@ class WorldTimer this.stepTime = 0; this.startedTime = (new Date()).getTime(); this.currentTime = this.startedTime; - this.validateProperties(); } - validateProperties() + startWorldSteps(world) { - if(!this.world){ - ErrorManager.error('Missing world parameter.'); + if(!world){ + Logger.error('World instance invalid.', {world}); + return; } - } - - startWorldSteps() - { - this.stepTime = 1000 * this.world.timeStep; + this.stepTime = 1000 * world.timeStep; if(this.clockInstance){ + Logger.debug('WorldTimes using clock instance.'); this.worldTimer = this.clockInstance.setInterval(() => { - this.setIntervalCallback(); + this.setIntervalCallback(world); }, this.stepTime); return; } + Logger.debug('WorldTimes using setInterval.'); this.worldTimer = setInterval(() => { - this.setIntervalCallback(); + this.setIntervalCallback(world); }, this.stepTime); } - setIntervalCallback() + setIntervalCallback(world) { if(this.paused){ return; } this.currentTime += this.stepTime; - this.stepWorld(); + this.stepWorld(world); this.executeCallbacks(); } - stepWorld() + stepWorld(world) { - if(this.world.useFixedWorldStep){ - this.world.step(this.world.timeStep); + if(world.useFixedWorldStep){ + world.step(world.timeStep); return; } - this.stepWorldWithSubSteps(); + this.stepWorldWithSubSteps(world); } executeCallbacks() @@ -73,11 +70,11 @@ class WorldTimer } } - stepWorldWithSubSteps() + stepWorldWithSubSteps(world) { let now = Date.now() / 1000; let timeSinceLastCall = now - this.lastCallTime; - this.world.step(this.world.timeStep, timeSinceLastCall, this.world.maxSubSteps); + world.step(world.timeStep, timeSinceLastCall, world.maxSubSteps); } } diff --git a/migrations/development/beta.30-sql-update.sql b/migrations/development/beta.30-sql-update.sql index be6639bdb..6135f4d7b 100644 --- a/migrations/development/beta.30-sql-update.sql +++ b/migrations/development/beta.30-sql-update.sql @@ -9,7 +9,6 @@ UPDATE `rooms` SET `map_filename` = CONCAT(`map_filename`, '.json'), `scene_images` = REPLACE(CONCAT(`scene_images`, '.png'), ',', '.png,'); - ####################################################################################################################### SET FOREIGN_KEY_CHECKS = 1; diff --git a/migrations/development/beta.31-sql-update.sql b/migrations/development/beta.31-sql-update.sql new file mode 100644 index 000000000..912e90eab --- /dev/null +++ b/migrations/development/beta.31-sql-update.sql @@ -0,0 +1,40 @@ +####################################################################################################################### + +SET FOREIGN_KEY_CHECKS = 0; + +####################################################################################################################### + +# Sample player fix: +REPLACE INTO `players_state` (`id`, `player_id`, `room_id`, `x`, `y`, `dir`) VALUES + (1, 1, 5, 332, 288, 'down'); + +REPLACE INTO `players_stats` (`id`, `player_id`, `stat_id`, `base_value`, `value`) VALUES + (1, 1, 1, 280, 81), + (2, 1, 2, 280, 85), + (3, 1, 3, 280, 400), + (4, 1, 4, 280, 280), + (5, 1, 5, 100, 100), + (6, 1, 6, 100, 100), + (7, 1, 7, 100, 100), + (8, 1, 8, 100, 100), + (9, 1, 9, 100, 100), + (10, 1, 10, 100, 100); + +# Config: +SET @boolean_id = (SELECT `id` FROM `config_types` WHERE `label` = 'boolean'); +INSERT INTO `config` (`scope`, `path`, `value`, `type`) VALUES ('server', 'players/physicsBody/usePlayerSpeedConfig', '0', @boolean_id); +INSERT INTO `config` (`scope`, `path`, `value`, `type`) VALUES ('server', 'players/physicsBody/usePlayerSpeedProperty', '0', @boolean_id); + +# Rooms: +UPDATE `rooms` SET `name` = 'reldens-forest' WHERE `name` = 'ReldensForest'; +UPDATE `rooms` SET `name` = 'reldens-house-1' WHERE `name` = 'ReldensHouse_1'; +UPDATE `rooms` SET `name` = 'reldens-house-2' WHERE `name` = 'ReldensHouse_2'; +UPDATE `rooms` SET `name` = 'reldens-town' WHERE `name` = 'ReldensTown'; +UPDATE `rooms` SET `name` = 'reldens-house-1-2d-floor' WHERE `name` = 'ReldensHouse_1b'; +UPDATE `rooms` SET `name` = 'reldens-gravity' WHERE `name` = 'TopDownRoom'; + +####################################################################################################################### + +SET FOREIGN_KEY_CHECKS = 1; + +####################################################################################################################### diff --git a/migrations/production/reldens-basic-config-v4.0.0.sql b/migrations/production/reldens-basic-config-v4.0.0.sql index ee1f1b90d..947a31d0e 100644 --- a/migrations/production/reldens-basic-config-v4.0.0.sql +++ b/migrations/production/reldens-basic-config-v4.0.0.sql @@ -386,7 +386,7 @@ REPLACE INTO `features` (`id`, `code`, `title`, `is_enabled`) VALUES (13, 'rewards', 'Rewards', 1), (14, 'snippets', 'Snippets', 1), (16, 'ads', 'Ads', 1), - (17, 'world', 'World', 1); + (17, 'world', 'World', 0); REPLACE INTO `items_types` (`id`, `key`) VALUES (10, 'base'), diff --git a/migrations/production/reldens-sample-data-v4.0.0.sql b/migrations/production/reldens-sample-data-v4.0.0.sql index 39e0b5950..6299e805e 100644 --- a/migrations/production/reldens-sample-data-v4.0.0.sql +++ b/migrations/production/reldens-sample-data-v4.0.0.sql @@ -90,10 +90,6 @@ REPLACE INTO `ads_event_video` (`id`, `ads_id`, `event_key`, `event_data`) VALUE (1, 5, 'activatedRoom_ReldensTown', '{"rewardItemKey":"coins","rewardItemQty":1}'), (2, 6, 'activatedRoom_ReldensForest', '{"rewardItemKey":"coins","rewardItemQty":1}'); -REPLACE INTO `ads_played` (`id`, `ads_id`, `player_id`, `started_at`, `ended_at`) VALUES - (7, 5, 1, '2023-10-17 18:28:17', '2023-10-17 18:28:22'), - (8, 6, 1, '2023-09-28 21:00:31', '2023-09-28 21:00:41'); - REPLACE INTO `ads_providers` (`id`, `key`, `enabled`) VALUES (1, 'crazyGames', 0), (2, 'gameMonetize', 0); @@ -518,7 +514,7 @@ REPLACE INTO `features` (`id`, `code`, `title`, `is_enabled`) VALUES (13, 'rewards', 'Rewards', 1), (14, 'snippets', 'Snippets', 1), (16, 'ads', 'Ads', 1), - (17, 'world', 'World', 1); + (17, 'world', 'World', 0); REPLACE 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), @@ -531,10 +527,10 @@ REPLACE INTO `items_group` (`id`, `key`, `label`, `description`, `files_name`, ` REPLACE 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', 10, 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, '{"animationData":{"frameWidth":64,"frameHeight":64,"start":6,"end":11,"repeat":0,"hide":true,"usePlayerPosition":true,"closeInventoryOnUse":true,"followPlayer":true,"startsOnTarget":true},"removeAfterUse":true}'), - (4, 'axe', 1, 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', 1, 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, '{"animationData":{"frameWidth":64,"frameHeight":64,"start":6,"end":11,"repeat":0,"hide":true,"usePlayerPosition":true,"closeInventoryOnUse":true,"followPlayer":true,"startsOnTarget":true},"removeAfterUse":true}'); + (3, 'heal_potion_20', 5, NULL, 'Heal Potion', 'A heal potion that will restore 20 HP.', 0, 1, NULL, NULL, '{"animationData":{"frameWidth":64,"frameHeight":64,"start":6,"end":11,"repeat":0,"usePlayerPosition":true,"followPlayer":true,"startsOnTarget":true},"removeAfterUse":true}'), + (4, 'axe', 1, 1, 'Axe', 'A short distance but powerful weapon.', 0, 0, NULL, NULL, '{"animationData":{"frameWidth":64,"frameHeight":64,"start":6,"end":11,"repeat":0,"destroyOnComplete":true,"usePlayerPosition":true,"followPlayer":true,"startsOnTarget":true}}'), + (5, 'spear', 1, 1, 'Spear', 'A short distance but powerful weapon.', 0, 0, NULL, NULL, '{"animationData":{"frameWidth":64,"frameHeight":64,"start":6,"end":11,"repeat":0,"destroyOnComplete":true,"usePlayerPosition":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, '{"animationData":{"frameWidth":64,"frameHeight":64,"start":6,"end":11,"repeat":0,"usePlayerPosition":true,"followPlayer":true,"startsOnTarget":true},"removeAfterUse":true}'); REPLACE INTO `items_item_modifiers` (`id`, `item_id`, `key`, `property_key`, `operation`, `value`, `maxProperty`) VALUES (1, 4, 'atk', 'stats/atk', 5, '5', NULL), @@ -675,12 +671,12 @@ REPLACE INTO `rewards` (`id`, `object_id`, `item_id`, `modifier_id`, `experience (2, 6, 2, NULL, 10, 100, 3, 0, 0, 1); REPLACE INTO `rooms` (`id`, `name`, `title`, `map_filename`, `scene_images`, `room_class_key`, `customData`) VALUES - (2, 'ReldensHouse_1', 'House - 1', 'reldens-house-1.json', 'reldens-house-1.png', NULL, NULL), - (3, 'ReldensHouse_2', 'House - 2', 'reldens-house-2.json', 'reldens-house-2.png', NULL, NULL), - (4, 'ReldensTown', 'Town', 'reldens-town.json', 'reldens-town.png', NULL, NULL), - (5, 'ReldensForest', 'Forest', 'reldens-forest.json', 'reldens-forest.png', NULL, NULL), - (6, 'ReldensHouse_1b', 'House - 1 - Floor 2', 'reldens-house-1-2d-floor.json', 'reldens-house-1-2d-floor.png', NULL, NULL), - (7, 'TopDownRoom', 'Gravity World!', 'reldens-gravity.json', 'reldens-gravity.png', NULL, '{"gravity":[0,625],"applyGravity":true,"allowPassWallsFromBelow":true,"timeStep":0.012,"type":"TOP_DOWN_WITH_GRAVITY","useFixedWorldStep":false,"maxSubSteps":5,"movementSpeed":200,"usePathFinder":false}'); + (2, 'reldens-house-1', 'House - 1', 'reldens-house-1.json', 'reldens-house-1.png', NULL, NULL), + (3, 'reldens-house-2', 'House - 2', 'reldens-house-2.json', 'reldens-house-2.png', NULL, NULL), + (4, 'reldens-town', 'Town', 'reldens-town.json', 'reldens-town.png', NULL, NULL), + (5, 'reldens-forest', 'Forest', 'reldens-forest.json', 'reldens-forest.png', NULL, NULL), + (6, 'reldens-house-1-2d-floor', 'House - 1 - Floor 2', 'reldens-house-1-2d-floor.json', 'reldens-house-1-2d-floor.png', NULL, NULL), + (7, 'reldens-gravity', 'Gravity World!', 'reldens-gravity.json', 'reldens-gravity.png', NULL, '{"gravity":[0,625],"applyGravity":true,"allowPassWallsFromBelow":true,"timeStep":0.012,"type":"TOP_DOWN_WITH_GRAVITY","useFixedWorldStep":false,"maxSubSteps":2,"movementSpeed":160,"usePathFinder":false}'); REPLACE INTO `rooms_change_points` (`id`, `room_id`, `tile_index`, `next_room_id`) VALUES (1, 2, 816, 4), @@ -699,7 +695,7 @@ REPLACE INTO `rooms_change_points` (`id`, `room_id`, `tile_index`, `next_room_id (14, 6, 664, 2), (15, 7, 540, 3), (16, 3, 500, 7), - (17, 3, 780, 7); + (17, 3, 780, 4); REPLACE INTO `rooms_return_points` (`id`, `room_id`, `direction`, `x`, `y`, `is_default`, `from_room_id`) VALUES (1, 2, 'up', 548, 615, 1, 4), diff --git a/package-lock.json b/package-lock.json index ab1950e72..93e2b7dc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "reldens", - "version": "4.0.0-beta.30", + "version": "4.0.0-beta.31", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "reldens", - "version": "4.0.0-beta.30", + "version": "4.0.0-beta.31", "license": "MIT", "dependencies": { "@adminjs/design-system": "2.1.2", @@ -54,11 +54,11 @@ "@parcel/transformer-svg": "2.10.3", "@parcel/transformer-webmanifest": "2.10.3", "@parcel/transformer-worklet": "2.10.3", - "@reldens/items-system": "^0.23.0", - "@reldens/modifiers": "^0.20.0", - "@reldens/skills": "^0.20.0", - "@reldens/storage": "^0.18.0", - "@reldens/utils": "^0.21.1", + "@reldens/items-system": "^0.25.0", + "@reldens/modifiers": "^0.21.0", + "@reldens/skills": "^0.22.0", + "@reldens/storage": "^0.19.0", + "@reldens/utils": "^0.22.0", "@sendgrid/mail": "7.7.0", "adminjs": "5.7.3", "bcrypt": "5.1.1", @@ -3023,16 +3023,16 @@ } }, "node_modules/@mikro-orm/core": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-5.9.3.tgz", - "integrity": "sha512-SYcRa4Rmi6GSPAvaXIsrBrhHLCieZtnFHYXtcUVMRpJRBxUbqv8RfhUedhYZGty8eTd44nNY9+zn6auv1x8IoA==", + "version": "5.9.7", + "resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-5.9.7.tgz", + "integrity": "sha512-VzbpJPQlwuK6Q/4FkppWNGKvzyYL31Gsw/qskr/GCa/010yLO8u3RQio/Q1EKRi+tNsjhqTPGA1b7OOM+DvpiQ==", "dependencies": { "acorn-loose": "8.3.0", "acorn-walk": "8.2.0", "dotenv": "16.3.1", "fs-extra": "11.1.1", "globby": "11.1.0", - "mikro-orm": "5.9.3", + "mikro-orm": "5.9.7", "reflect-metadata": "0.1.13" }, "engines": { @@ -3087,9 +3087,9 @@ } }, "node_modules/@mikro-orm/mongodb": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/@mikro-orm/mongodb/-/mongodb-5.9.3.tgz", - "integrity": "sha512-ltuBs6dwJz1p1TCuIunyCmvAhMwa5o3NeTe9KFIbJYXoUQGqMYLUcHFKGe9+KYsIAz0K6j3kMUGbL1xKGax8Sg==", + "version": "5.9.7", + "resolved": "https://registry.npmjs.org/@mikro-orm/mongodb/-/mongodb-5.9.7.tgz", + "integrity": "sha512-KKV8IsN7yDXDEDOXhVNw1Labm8Z9Sg0KsGBY45fUUlaPSMKJQcmoV9hFYg1fKh5Hab6EG5DEOcFGj5Q0JINs2w==", "dependencies": { "bson": "^5.4.0", "mongodb": "5.8.1" @@ -3173,9 +3173,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", - "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.3.tgz", + "integrity": "sha512-SyCxhJfmK6MoLNV5SbDpNdUy9SDv5H7y9/9rl3KpnwgTHWuNNMc87zWqbcIZXNWY+aUjxLGLEcvHoLagG4tWCg==", "optional": true, "dependencies": { "sparse-bitfield": "^3.0.3" @@ -5005,50 +5005,66 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@reldens/items-system": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@reldens/items-system/-/items-system-0.25.0.tgz", + "integrity": "sha512-+gNO0Ng49GbzIAC3LhJf0CklwGPDORaIpNPOsNMHLBPXV/FljZmbzBYEyH8odasyJW/VoDDHqcCXvXXowsKSMQ==", + "dependencies": { + "@reldens/modifiers": "0.21.0", + "@reldens/storage": "^0.19.0", + "@reldens/utils": "^0.23.0" + } + }, + "node_modules/@reldens/items-system/node_modules/@reldens/utils": { "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@reldens/items-system/-/items-system-0.23.0.tgz", - "integrity": "sha512-9UixVYzHk1jZsRWZelIBLyEF2CPMYXPets2R/VYWXRrukFvkNbb8w9nWYY5f6ANAnRr+uH/79SbGMMfGMdLU9w==", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.23.0.tgz", + "integrity": "sha512-oZLsZZ4xthEJJThXVlth4fswEQ816u+N2qsjpGs9Q+Gq9GrFAX65tWesyjIIYOJPj4iloDFZTNUGo/j5sHwIkg==", "dependencies": { - "@reldens/modifiers": "0.20.0", - "@reldens/storage": "^0.18.0", - "@reldens/utils": "^0.21.1" + "await-event-emitter": "^2.0.2" } }, "node_modules/@reldens/modifiers": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@reldens/modifiers/-/modifiers-0.20.0.tgz", - "integrity": "sha512-pHNhvpHxWJeEjB8hvouMSaQDbTAmhX0RM/joe8O0Jc96tKJp3y+LMuOqZVw2UbsCiyakwIpz5iSz6WJBe6ZHkQ==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@reldens/modifiers/-/modifiers-0.21.0.tgz", + "integrity": "sha512-1btXX0cLE5GB1NJ/cCZRYbVVDaQBDJYsbenSICy0IryjEyvXLd7ej48ccLiBVBwk2A/6PR6+95WoSiV/d8ea6Q==", "dependencies": { - "@reldens/utils": "^0.21.0" + "@reldens/utils": "^0.22.0" } }, "node_modules/@reldens/skills": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@reldens/skills/-/skills-0.20.0.tgz", - "integrity": "sha512-fDufgiBsB0yXA4oWHzX+8zZmMBCq0KdcUCEETmbREICd/dWVOF3fUaBUPqwclLHf0k1yt49VF9ayD1+CrdN7LQ==", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@reldens/skills/-/skills-0.22.0.tgz", + "integrity": "sha512-qHsOzJHW0PB+FT93pVLKylpXwAU2LUleb9TINVKrT2SMOUwSYnfP4I7xcVr7nfxQew4lbcxastwhgMrHRJNJ/Q==", + "dependencies": { + "@reldens/modifiers": "^0.21.0", + "@reldens/storage": "^0.19.0", + "@reldens/utils": "^0.23.0" + } + }, + "node_modules/@reldens/skills/node_modules/@reldens/utils": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.23.0.tgz", + "integrity": "sha512-oZLsZZ4xthEJJThXVlth4fswEQ816u+N2qsjpGs9Q+Gq9GrFAX65tWesyjIIYOJPj4iloDFZTNUGo/j5sHwIkg==", "dependencies": { - "@reldens/modifiers": "^0.20.0", - "@reldens/storage": "^0.18.0", - "@reldens/utils": "^0.21.0" + "await-event-emitter": "^2.0.2" } }, "node_modules/@reldens/storage": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.18.0.tgz", - "integrity": "sha512-MuUIlzOzv3HifVEL8mGXZZJv+2w5/e4JrmMfaLGIRlujwPAAOY0A/3Gvy0g4PxLA8TUSsh0ETACtZNYwYZzDCw==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.19.0.tgz", + "integrity": "sha512-PzB8ojymvuzUAlgSmukD/z3iAXSUe2NT4mXe2RvloumAa8yEJfPBoN1N2La0y5Uniu0K+cMXHmLuo53rLI9DsQ==", "dependencies": { "@mikro-orm/core": "^5.9.3", "@mikro-orm/mongodb": "^5.9.3", - "@reldens/utils": "^0.21.0", - "knex": "^3.0.1", + "@reldens/utils": "^0.22.0", + "knex": "^3.1.0", "mysql": "^2.18.1", - "objection": "^3.1.2" + "objection": "^3.1.3" } }, "node_modules/@reldens/utils": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.21.1.tgz", - "integrity": "sha512-APamwB56m2FgAxdLaa59yXEb9TcCmZ7FubKHElf7QaJ16l1AHEvcFRUbv24O7RdKqsGpTpqHRCfcn+lO+s2Ukg==", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.22.0.tgz", + "integrity": "sha512-khf38VbfMvA6No/OowkCVTMxrObSPi8uVNz330psznMTXJzPn5ZPHEYA9VdMKC5ozKhCi8Ug4SNI+e/jXXKyMA==", "dependencies": { "await-event-emitter": "^2.0.2" } @@ -5683,9 +5699,9 @@ } }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "bin": { "acorn": "bin/acorn" }, @@ -5779,9 +5795,9 @@ } }, "node_modules/ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -6349,9 +6365,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001570", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz", - "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==", + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", "funding": [ { "type": "opencollective", @@ -7630,9 +7646,9 @@ "optional": true }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "dependencies": { "reusify": "^1.0.4" } @@ -8415,9 +8431,9 @@ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "engines": { "node": ">= 4" } @@ -8937,9 +8953,9 @@ } }, "node_modules/knex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/knex/-/knex-3.0.1.tgz", - "integrity": "sha512-ruASxC6xPyDklRdrcDy6a9iqK+R9cGK214aiQa+D9gX2ZnHZKv6o6JC9ZfgxILxVAul4bZ13c3tgOAHSuQ7/9g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz", + "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==", "dependencies": { "colorette": "2.0.19", "commander": "^10.0.0", @@ -8950,7 +8966,7 @@ "getopts": "2.3.0", "interpret": "^2.2.0", "lodash": "^4.17.21", - "pg-connection-string": "2.6.1", + "pg-connection-string": "2.6.2", "rechoir": "^0.8.0", "resolve-from": "^5.0.0", "tarn": "^3.0.2", @@ -9405,9 +9421,9 @@ } }, "node_modules/mikro-orm": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-5.9.3.tgz", - "integrity": "sha512-lLBWENtV7yUE5KraqJEMaaKDPotnab6i/uf+wOyjILxYPjaXivH+oq7g9U3WS7K1fLUpQlR+bdQTOExHLy1FtQ==", + "version": "5.9.7", + "resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-5.9.7.tgz", + "integrity": "sha512-0AxNDxQWk45n5N5g5q/K2tVj1/Narf4h5+1fhFc0uYAp/tOGAGvjmVK43Xy4TisEm/1VpBNOtS7FYKvh14WVOQ==", "engines": { "node": ">= 14.0.0" } @@ -9838,11 +9854,11 @@ } }, "node_modules/objection": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/objection/-/objection-3.1.2.tgz", - "integrity": "sha512-V8YwRWz+DFbB9JS/m7TBLhRPVAFK/VX7yV3ZDAMkfUG9qYHLRyG/K4ZS0acKtGPWtRdVrCuN4qM1VkH3PuJ5Lg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/objection/-/objection-3.1.3.tgz", + "integrity": "sha512-X4DH8/xKBS34bwWOSLAPyceg0JgLhLiUuz+cEEyDA8iDFoT1UM9UbtwBpwHV11hYskAKxOgVlNHeveFQiOPDXA==", "dependencies": { - "ajv": "^8.6.2", + "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "db-errors": "^0.2.3" }, @@ -10080,9 +10096,9 @@ } }, "node_modules/pg-connection-string": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", - "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" }, "node_modules/phaser": { "version": "3.70.0", @@ -14335,23 +14351,23 @@ } }, "@mikro-orm/core": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-5.9.3.tgz", - "integrity": "sha512-SYcRa4Rmi6GSPAvaXIsrBrhHLCieZtnFHYXtcUVMRpJRBxUbqv8RfhUedhYZGty8eTd44nNY9+zn6auv1x8IoA==", + "version": "5.9.7", + "resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-5.9.7.tgz", + "integrity": "sha512-VzbpJPQlwuK6Q/4FkppWNGKvzyYL31Gsw/qskr/GCa/010yLO8u3RQio/Q1EKRi+tNsjhqTPGA1b7OOM+DvpiQ==", "requires": { "acorn-loose": "8.3.0", "acorn-walk": "8.2.0", "dotenv": "16.3.1", "fs-extra": "11.1.1", "globby": "11.1.0", - "mikro-orm": "5.9.3", + "mikro-orm": "5.9.7", "reflect-metadata": "0.1.13" } }, "@mikro-orm/mongodb": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/@mikro-orm/mongodb/-/mongodb-5.9.3.tgz", - "integrity": "sha512-ltuBs6dwJz1p1TCuIunyCmvAhMwa5o3NeTe9KFIbJYXoUQGqMYLUcHFKGe9+KYsIAz0K6j3kMUGbL1xKGax8Sg==", + "version": "5.9.7", + "resolved": "https://registry.npmjs.org/@mikro-orm/mongodb/-/mongodb-5.9.7.tgz", + "integrity": "sha512-KKV8IsN7yDXDEDOXhVNw1Labm8Z9Sg0KsGBY45fUUlaPSMKJQcmoV9hFYg1fKh5Hab6EG5DEOcFGj5Q0JINs2w==", "requires": { "bson": "^5.4.0", "mongodb": "5.8.1" @@ -14381,9 +14397,9 @@ } }, "@mongodb-js/saslprep": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", - "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.3.tgz", + "integrity": "sha512-SyCxhJfmK6MoLNV5SbDpNdUy9SDv5H7y9/9rl3KpnwgTHWuNNMc87zWqbcIZXNWY+aUjxLGLEcvHoLagG4tWCg==", "optional": true, "requires": { "sparse-bitfield": "^3.0.3" @@ -15487,50 +15503,70 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "@reldens/items-system": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@reldens/items-system/-/items-system-0.23.0.tgz", - "integrity": "sha512-9UixVYzHk1jZsRWZelIBLyEF2CPMYXPets2R/VYWXRrukFvkNbb8w9nWYY5f6ANAnRr+uH/79SbGMMfGMdLU9w==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@reldens/items-system/-/items-system-0.25.0.tgz", + "integrity": "sha512-+gNO0Ng49GbzIAC3LhJf0CklwGPDORaIpNPOsNMHLBPXV/FljZmbzBYEyH8odasyJW/VoDDHqcCXvXXowsKSMQ==", "requires": { - "@reldens/modifiers": "0.20.0", - "@reldens/storage": "^0.18.0", - "@reldens/utils": "^0.21.1" + "@reldens/modifiers": "0.21.0", + "@reldens/storage": "^0.19.0", + "@reldens/utils": "^0.23.0" + }, + "dependencies": { + "@reldens/utils": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.23.0.tgz", + "integrity": "sha512-oZLsZZ4xthEJJThXVlth4fswEQ816u+N2qsjpGs9Q+Gq9GrFAX65tWesyjIIYOJPj4iloDFZTNUGo/j5sHwIkg==", + "requires": { + "await-event-emitter": "^2.0.2" + } + } } }, "@reldens/modifiers": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@reldens/modifiers/-/modifiers-0.20.0.tgz", - "integrity": "sha512-pHNhvpHxWJeEjB8hvouMSaQDbTAmhX0RM/joe8O0Jc96tKJp3y+LMuOqZVw2UbsCiyakwIpz5iSz6WJBe6ZHkQ==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@reldens/modifiers/-/modifiers-0.21.0.tgz", + "integrity": "sha512-1btXX0cLE5GB1NJ/cCZRYbVVDaQBDJYsbenSICy0IryjEyvXLd7ej48ccLiBVBwk2A/6PR6+95WoSiV/d8ea6Q==", "requires": { - "@reldens/utils": "^0.21.0" + "@reldens/utils": "^0.22.0" } }, "@reldens/skills": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@reldens/skills/-/skills-0.20.0.tgz", - "integrity": "sha512-fDufgiBsB0yXA4oWHzX+8zZmMBCq0KdcUCEETmbREICd/dWVOF3fUaBUPqwclLHf0k1yt49VF9ayD1+CrdN7LQ==", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@reldens/skills/-/skills-0.22.0.tgz", + "integrity": "sha512-qHsOzJHW0PB+FT93pVLKylpXwAU2LUleb9TINVKrT2SMOUwSYnfP4I7xcVr7nfxQew4lbcxastwhgMrHRJNJ/Q==", "requires": { - "@reldens/modifiers": "^0.20.0", - "@reldens/storage": "^0.18.0", - "@reldens/utils": "^0.21.0" + "@reldens/modifiers": "^0.21.0", + "@reldens/storage": "^0.19.0", + "@reldens/utils": "^0.23.0" + }, + "dependencies": { + "@reldens/utils": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.23.0.tgz", + "integrity": "sha512-oZLsZZ4xthEJJThXVlth4fswEQ816u+N2qsjpGs9Q+Gq9GrFAX65tWesyjIIYOJPj4iloDFZTNUGo/j5sHwIkg==", + "requires": { + "await-event-emitter": "^2.0.2" + } + } } }, "@reldens/storage": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.18.0.tgz", - "integrity": "sha512-MuUIlzOzv3HifVEL8mGXZZJv+2w5/e4JrmMfaLGIRlujwPAAOY0A/3Gvy0g4PxLA8TUSsh0ETACtZNYwYZzDCw==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@reldens/storage/-/storage-0.19.0.tgz", + "integrity": "sha512-PzB8ojymvuzUAlgSmukD/z3iAXSUe2NT4mXe2RvloumAa8yEJfPBoN1N2La0y5Uniu0K+cMXHmLuo53rLI9DsQ==", "requires": { "@mikro-orm/core": "^5.9.3", "@mikro-orm/mongodb": "^5.9.3", - "@reldens/utils": "^0.21.0", - "knex": "^3.0.1", + "@reldens/utils": "^0.22.0", + "knex": "^3.1.0", "mysql": "^2.18.1", - "objection": "^3.1.2" + "objection": "^3.1.3" } }, "@reldens/utils": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.21.1.tgz", - "integrity": "sha512-APamwB56m2FgAxdLaa59yXEb9TcCmZ7FubKHElf7QaJ16l1AHEvcFRUbv24O7RdKqsGpTpqHRCfcn+lO+s2Ukg==", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@reldens/utils/-/utils-0.22.0.tgz", + "integrity": "sha512-khf38VbfMvA6No/OowkCVTMxrObSPi8uVNz330psznMTXJzPn5ZPHEYA9VdMKC5ozKhCi8Ug4SNI+e/jXXKyMA==", "requires": { "await-event-emitter": "^2.0.2" } @@ -16003,9 +16039,9 @@ } }, "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==" + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==" }, "acorn-loose": { "version": "8.3.0", @@ -16083,9 +16119,9 @@ } }, "ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -16516,9 +16552,9 @@ "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==" }, "caniuse-lite": { - "version": "1.0.30001570", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz", - "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==" + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==" }, "chalk": { "version": "4.1.2", @@ -17470,9 +17506,9 @@ "optional": true }, "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "requires": { "reusify": "^1.0.4" } @@ -18048,9 +18084,9 @@ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==" }, "immutable": { "version": "4.1.0", @@ -18412,9 +18448,9 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "knex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/knex/-/knex-3.0.1.tgz", - "integrity": "sha512-ruASxC6xPyDklRdrcDy6a9iqK+R9cGK214aiQa+D9gX2ZnHZKv6o6JC9ZfgxILxVAul4bZ13c3tgOAHSuQ7/9g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz", + "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==", "requires": { "colorette": "2.0.19", "commander": "^10.0.0", @@ -18425,7 +18461,7 @@ "getopts": "2.3.0", "interpret": "^2.2.0", "lodash": "^4.17.21", - "pg-connection-string": "2.6.1", + "pg-connection-string": "2.6.2", "rechoir": "^0.8.0", "resolve-from": "^5.0.0", "tarn": "^3.0.2", @@ -18684,9 +18720,9 @@ } }, "mikro-orm": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-5.9.3.tgz", - "integrity": "sha512-lLBWENtV7yUE5KraqJEMaaKDPotnab6i/uf+wOyjILxYPjaXivH+oq7g9U3WS7K1fLUpQlR+bdQTOExHLy1FtQ==" + "version": "5.9.7", + "resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-5.9.7.tgz", + "integrity": "sha512-0AxNDxQWk45n5N5g5q/K2tVj1/Narf4h5+1fhFc0uYAp/tOGAGvjmVK43Xy4TisEm/1VpBNOtS7FYKvh14WVOQ==" }, "mime": { "version": "3.0.0", @@ -19008,11 +19044,11 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "objection": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/objection/-/objection-3.1.2.tgz", - "integrity": "sha512-V8YwRWz+DFbB9JS/m7TBLhRPVAFK/VX7yV3ZDAMkfUG9qYHLRyG/K4ZS0acKtGPWtRdVrCuN4qM1VkH3PuJ5Lg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/objection/-/objection-3.1.3.tgz", + "integrity": "sha512-X4DH8/xKBS34bwWOSLAPyceg0JgLhLiUuz+cEEyDA8iDFoT1UM9UbtwBpwHV11hYskAKxOgVlNHeveFQiOPDXA==", "requires": { - "ajv": "^8.6.2", + "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "db-errors": "^0.2.3" } @@ -19180,9 +19216,9 @@ } }, "pg-connection-string": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", - "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" }, "phaser": { "version": "3.70.0", diff --git a/package.json b/package.json index 242bc2e58..68a62882a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "reldens", - "version": "4.0.0-beta.30", + "version": "4.0.0-beta.31", "description": "Reldens - MMORPG Platform", "author": "Damian A. Pastorini", "license": "MIT", @@ -79,11 +79,11 @@ "@parcel/transformer-svg": "2.10.3", "@parcel/transformer-webmanifest": "2.10.3", "@parcel/transformer-worklet": "2.10.3", - "@reldens/items-system": "^0.23.0", - "@reldens/modifiers": "^0.20.0", - "@reldens/skills": "^0.20.0", - "@reldens/storage": "^0.18.0", - "@reldens/utils": "^0.21.1", + "@reldens/items-system": "^0.25.0", + "@reldens/modifiers": "^0.21.0", + "@reldens/skills": "^0.22.0", + "@reldens/storage": "^0.19.0", + "@reldens/utils": "^0.22.0", "@sendgrid/mail": "7.7.0", "adminjs": "5.7.3", "bcrypt": "5.1.1", diff --git a/theme/default/assets/custom/sprites/heal-potion-20.png b/theme/default/assets/custom/sprites/heal_potion_20.png similarity index 100% rename from theme/default/assets/custom/sprites/heal-potion-20.png rename to theme/default/assets/custom/sprites/heal_potion_20.png diff --git a/theme/default/assets/custom/sprites/magic-potion-20.png b/theme/default/assets/custom/sprites/magic_potion_20.png similarity index 100% rename from theme/default/assets/custom/sprites/magic-potion-20.png rename to theme/default/assets/custom/sprites/magic_potion_20.png diff --git a/theme/default/assets/maps/reldens-gravity.json b/theme/default/assets/maps/reldens-gravity.json index ca0298e81..32987496c 100644 --- a/theme/default/assets/maps/reldens-gravity.json +++ b/theme/default/assets/maps/reldens-gravity.json @@ -77,7 +77,7 @@ "firstgid":1, "image":"reldens-gravity.png", "imageheight":32, - "imagewidth":102, + "imagewidth":100, "margin":0, "name":"reldens-gravity", "spacing":2, diff --git a/theme/default/assets/maps/reldens-gravity.png b/theme/default/assets/maps/reldens-gravity.png index 02a760d3a..337aeb448 100644 Binary files a/theme/default/assets/maps/reldens-gravity.png and b/theme/default/assets/maps/reldens-gravity.png differ diff --git a/theme/default/es-index.html b/theme/default/es-index.html index 54f101a4d..f16828ac2 100644 --- a/theme/default/es-index.html +++ b/theme/default/es-index.html @@ -1,211 +1,213 @@ - + DwDeveloper - Reldens | MMORPG Plataforma + + -
-
- -

- - reldens - demo - -
- tu logo aqui - -

- -
-
-
-
- Descargo: Reldens no es solo un juego, es una plataforma para crear juegos. -
Esta es una demostración para mostrar cuántas funciones están disponibles en la plataforma. -
+
+
+ +

+ - reldens - demo - +
- tu logo aqui - +

+ +
+
+
+
+ Descargo: Reldens no es solo un juego, es una plataforma para crear juegos. +
Esta es una demostración para mostrar cuántas funciones están disponibles en la plataforma.
-
-
-
-
-

Crear Cuenta

-
- - -
-
- - -
-
- - -
-
- - -
- -
- -
- -
-
-
- -
-
-
- -
-