diff --git a/app/imports/api/creature/creatureProperties/CreatureProperties.js b/app/imports/api/creature/creatureProperties/CreatureProperties.js index 33183ddc..e4cc728e 100644 --- a/app/imports/api/creature/creatureProperties/CreatureProperties.js +++ b/app/imports/api/creature/creatureProperties/CreatureProperties.js @@ -82,6 +82,13 @@ const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({ index: 1, removeBeforeCompute: true, }, + // When this is true on any property, the creature needs to be recomputed + dirty: { + type: Boolean, + // Default to true because new properties cause a recomputation + defaultValue: true, + optional: true, + }, }); CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema); diff --git a/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js b/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js index b9958fcb..06ca6459 100644 --- a/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js +++ b/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js @@ -4,7 +4,6 @@ import SimpleSchema from 'simpl-schema'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const adjustQuantity = new ValidatedMethod({ name: 'creatureProperties.adjustQuantity', @@ -29,10 +28,6 @@ const adjustQuantity = new ValidatedMethod({ // Do work adjustQuantityWork({property, operation, value}); - - // Changing quantity does not change dependencies, but recomputing the - // inventory changes many deps at once, so recompute fully - computeCreature(rootCreature._id); }, }); @@ -47,7 +42,7 @@ export function adjustQuantityWork({property, operation, value}){ } if (operation === 'set'){ CreatureProperties.update(property._id, { - $set: {quantity: value} + $set: {quantity: value, dirty: true} }, { selector: property }); @@ -57,7 +52,8 @@ export function adjustQuantityWork({property, operation, value}){ let currentQuantity = property.quantity; if (currentQuantity + value < 0) value = -currentQuantity; CreatureProperties.update(property._id, { - $inc: {quantity: value} + $inc: { quantity: value }, + $set: { dirty: true } }, { selector: property }); diff --git a/app/imports/api/creature/creatureProperties/methods/damageProperty.js b/app/imports/api/creature/creatureProperties/methods/damageProperty.js index 51c37749..315b6f2c 100644 --- a/app/imports/api/creature/creatureProperties/methods/damageProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/damageProperty.js @@ -4,7 +4,6 @@ import SimpleSchema from 'simpl-schema'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const damageProperty = new ValidatedMethod({ name: 'creatureProperties.damage', @@ -38,7 +37,6 @@ const damageProperty = new ValidatedMethod({ ); } let result = damagePropertyWork({ property, operation, value }); - computeCreature(rootCreature._id); return result; }, }); @@ -69,7 +67,7 @@ export function damagePropertyWork({property, operation, value}){ // Write the results CreatureProperties.update(property._id, { - $set: {damage, value: newValue} + $set: {damage, value: newValue, dirty: true} }, { selector: property }); diff --git a/app/imports/api/creature/creatureProperties/methods/dealDamage.js b/app/imports/api/creature/creatureProperties/methods/dealDamage.js index b93aa495..ddb3dcc1 100644 --- a/app/imports/api/creature/creatureProperties/methods/dealDamage.js +++ b/app/imports/api/creature/creatureProperties/methods/dealDamage.js @@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur import Creatures from '/imports/api/creature/creatures/Creatures.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const dealDamage = new ValidatedMethod({ name: 'creatureProperties.dealDamage', @@ -33,7 +32,6 @@ const dealDamage = new ValidatedMethod({ assertEditPermission(creature, this.userId); const totalDamage = dealDamageWork({creature, damageType, amount}) - computeCreature(creatureId); return totalDamage; }, }); diff --git a/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js b/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js index 03c505c2..3e640714 100644 --- a/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js @@ -9,7 +9,6 @@ import { renewDocIds } from '/imports/api/parenting/parenting.js'; import { reorderDocs } from '/imports/api/parenting/order.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; var snackbar; if (Meteor.isClient){ snackbar = require( @@ -77,6 +76,9 @@ const duplicateProperty = new ValidatedMethod({ // Order the root node property.order += 0.5; + + // Mark the sheet as needing recompute + property.dirty = true; // Insert the properties CreatureProperties.batchInsert([property, ...nodes]); @@ -87,9 +89,6 @@ const duplicateProperty = new ValidatedMethod({ ancestorId: property.ancestors[0].id, }); - // Inserting a creature property invalidates dependencies: full recompute - computeCreature(creature._id); - return propertyId; }, }); diff --git a/app/imports/api/creature/creatureProperties/methods/equipItem.js b/app/imports/api/creature/creatureProperties/methods/equipItem.js index 24cb51fa..a8bbb911 100644 --- a/app/imports/api/creature/creatureProperties/methods/equipItem.js +++ b/app/imports/api/creature/creatureProperties/methods/equipItem.js @@ -4,7 +4,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { organizeDoc } from '/imports/api/parenting/organizeMethods.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js'; import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js'; @@ -29,7 +28,7 @@ const equipItem = new ValidatedMethod({ let creature = getRootCreatureAncestor(item); assertEditPermission(creature, this.userId); CreatureProperties.update(_id, { - $set: {equipped}, + $set: { equipped, dirty: true }, }, { selector: {type: 'item'}, }); @@ -46,8 +45,6 @@ const equipItem = new ValidatedMethod({ order: Number.MAX_SAFE_INTEGER, skipRecompute: true, }); - - computeCreature(creature._id); }, }); diff --git a/app/imports/api/creature/creatureProperties/methods/flipToggle.js b/app/imports/api/creature/creatureProperties/methods/flipToggle.js index e39d0c75..df563cdc 100644 --- a/app/imports/api/creature/creatureProperties/methods/flipToggle.js +++ b/app/imports/api/creature/creatureProperties/methods/flipToggle.js @@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const flipToggle = new ValidatedMethod({ name: 'creatureProperties.flipToggle', @@ -36,12 +35,10 @@ const flipToggle = new ValidatedMethod({ CreatureProperties.update(_id, {$set: { enabled: !currentValue, disabled: currentValue, + dirty: true, }}, { selector: {type: 'toggle'}, }); - - // Updating a toggle is likely to change the whole tree, do a full recompute - computeCreature(rootCreature._id); }, }); diff --git a/app/imports/api/creature/creatureProperties/methods/insertProperty.js b/app/imports/api/creature/creatureProperties/methods/insertProperty.js index 4535e1fe..5d3567b0 100644 --- a/app/imports/api/creature/creatureProperties/methods/insertProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/insertProperty.js @@ -5,7 +5,6 @@ import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/ge import SimpleSchema from 'simpl-schema'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { reorderDocs } from '/imports/api/parenting/order.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import { getAncestry } from '/imports/api/parenting/parenting.js'; import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js'; import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; @@ -132,14 +131,13 @@ const insertPropertyAsChildOfTag = new ValidatedMethod({ export function insertPropertyWork({property, creature}){ delete property._id; + property.dirty = true; let _id = CreatureProperties.insert(property); // Tree structure changed by insert, reorder the tree reorderDocs({ collection: CreatureProperties, ancestorId: creature._id, }); - // Inserting a creature property invalidates dependencies: full recompute - computeCreature(creature._id); return _id; } diff --git a/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js index e73cbe33..1360ffc3 100644 --- a/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js +++ b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js @@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { setLineageOfDocs, @@ -71,9 +70,6 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({ collection: CreatureProperties, ancestorId: rootCreature._id, }); - - // Inserting a creature property invalidates dependencies: full recompute - computeCreature(rootCreature._id); // Return the docId of the last property, the inserted root property return rootId; }, @@ -135,6 +131,9 @@ function insertPropertyFromNode(nodeId, ancestors, order){ node.order = order; } + // Mark root as dirty + node.dirty = true; + // Insert the creature properties CreatureProperties.batchInsert(nodes); return node; diff --git a/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js b/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js index fa2acf78..3d8f99ea 100644 --- a/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js @@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const pullFromProperty = new ValidatedMethod({ name: 'creatureProperties.pull', @@ -21,15 +20,12 @@ const pullFromProperty = new ValidatedMethod({ // Do work CreatureProperties.update(_id, { - $pull: {[path.join('.')]: {_id: itemId}}, + $pull: { [path.join('.')]: { _id: itemId } }, + $set: { dirty: true } }, { selector: {type: property.type}, getAutoValues: false, }); - - // TODO figure out if this method can change deps or not - computeCreature(rootCreature._id); - // recomputePropertyDependencies(property); } }); diff --git a/app/imports/api/creature/creatureProperties/methods/pushToProperty.js b/app/imports/api/creature/creatureProperties/methods/pushToProperty.js index e730065c..0ec4719c 100644 --- a/app/imports/api/creature/creatureProperties/methods/pushToProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/pushToProperty.js @@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import { get } from 'lodash'; const pushToProperty = new ValidatedMethod({ @@ -39,13 +38,11 @@ const pushToProperty = new ValidatedMethod({ // Do work CreatureProperties.update(_id, { - $push: {[joinedPath]: value}, + $push: { [joinedPath]: value }, + $set: { dirty: true }, }, { selector: {type: property.type}, }); - - // TODO figure out if this method can change deps or not - computeCreature(rootCreature._id); } }); diff --git a/app/imports/api/creature/creatureProperties/methods/restoreProperty.js b/app/imports/api/creature/creatureProperties/methods/restoreProperty.js index b612e6c7..02ed7637 100644 --- a/app/imports/api/creature/creatureProperties/methods/restoreProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/restoreProperty.js @@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { restore } from '/imports/api/parenting/softRemove.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const restoreProperty = new ValidatedMethod({ name: 'creatureProperties.restore', @@ -24,10 +23,13 @@ const restoreProperty = new ValidatedMethod({ assertEditPermission(rootCreature, this.userId); // Do work - restore({_id, collection: CreatureProperties}); - - // Changes dependency tree by restoring children - computeCreature(rootCreature._id); + restore({ + _id, + collection: CreatureProperties, + extraUpdates: { + $set: { dirty: true } + }, + }); } }); diff --git a/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js b/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js index c1e4baa3..4f460287 100644 --- a/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js +++ b/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js @@ -4,7 +4,6 @@ import SimpleSchema from 'simpl-schema'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const selectAmmoItem = new ValidatedMethod({ name: 'creatureProperties.selectAmmoItem', @@ -37,15 +36,10 @@ const selectAmmoItem = new ValidatedMethod({ } let path = `resources.itemsConsumed.${itemConsumedIndex}.itemId`; CreatureProperties.update(actionId, { - $set: {[path]: itemId} + $set: { [path]: itemId, dirty: true } }, { selector: action, }); - - // Changing the linked item does change the dependency tree - // TODO: We can predict exactly which deps will be affected instead of - // recomputing the entire creature - computeCreature(rootCreature._id); }, }); diff --git a/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js b/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js index 86df9247..96110e8e 100644 --- a/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js @@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { softRemove } from '/imports/api/parenting/softRemove.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const softRemoveProperty = new ValidatedMethod({ name: 'creatureProperties.softRemove', @@ -25,9 +24,6 @@ const softRemoveProperty = new ValidatedMethod({ // Do work softRemove({_id, collection: CreatureProperties}); - - // Changes dependency tree by removing children - computeCreature(rootCreature._id); } }); diff --git a/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js b/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js index d53ddc6e..d6bf733c 100644 --- a/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js @@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const updateCreatureProperty = new ValidatedMethod({ name: 'creatureProperties.update', @@ -37,17 +36,13 @@ const updateCreatureProperty = new ValidatedMethod({ let modifier; // unset empty values if (value === null || value === undefined){ - modifier = {$unset: {[pathString]: 1}}; + modifier = { $unset: {[pathString]: 1}, $set: { dirty: true } }; } else { - modifier = {$set: {[pathString]: value}}; + modifier = { $set: {[pathString]: value, dirty: true } }; } CreatureProperties.update(_id, modifier, { selector: {type: property.type}, }); - - // Updating a property is likely to change dependencies, do a full recompute - // denormalised stats might change, so fetch the creature again - computeCreature(rootCreature._id); }, }); diff --git a/app/imports/api/creature/creatures/Creatures.js b/app/imports/api/creature/creatures/Creatures.js index 1c118fee..ef05ff40 100644 --- a/app/imports/api/creature/creatures/Creatures.js +++ b/app/imports/api/creature/creatures/Creatures.js @@ -100,6 +100,11 @@ let CreatureSchema = new SimpleSchema({ type: SimpleSchema.Integer, defaultValue: 0, }, + // Does the character need a recompute? + dirty: { + type: Boolean, + optional: true, + }, // Version of computation engine that was last used to compute this creature computeVersion: { type: String, diff --git a/app/imports/api/creature/creatures/methods/restCreature.js b/app/imports/api/creature/creatures/methods/restCreature.js index 07a607ee..c75e3cd8 100644 --- a/app/imports/api/creature/creatures/methods/restCreature.js +++ b/app/imports/api/creature/creatures/methods/restCreature.js @@ -4,10 +4,9 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const restCreature = new ValidatedMethod({ - name: 'creature.methods.longRest', + name: 'creature.methods.rest', validate: new SimpleSchema({ creatureId: { type: String, @@ -51,7 +50,10 @@ const restCreature = new ValidatedMethod({ // update all attribute's damage filter.type = 'attribute'; CreatureProperties.update(filter, { - $set: {damage: 0} + $set: { + damage: 0, + dirty: true, + } }, { selector: {type: 'attribute'}, multi: true, @@ -63,7 +65,10 @@ const restCreature = new ValidatedMethod({ 'spell' ]}; CreatureProperties.update(filter, { - $set: {usesUsed: 0} + $set: { + usesUsed: 0, + dirty: true, + } }, { selector: {type: 'action'}, multi: true, @@ -103,13 +108,15 @@ const restCreature = new ValidatedMethod({ recoverableHd -= amountToRecover; resultingDamage = hd.damage - amountToRecover; CreatureProperties.update(hd._id, { - $set: {damage: resultingDamage} + $set: { + damage: resultingDamage, + dirty: true, + } }, { selector: {type: 'attribute'}, }); }); } - computeCreature(creatureId); }, }); diff --git a/app/imports/api/creature/experience/Experiences.js b/app/imports/api/creature/experience/Experiences.js index 7c0bb002..60501d02 100644 --- a/app/imports/api/creature/experience/Experiences.js +++ b/app/imports/api/creature/experience/Experiences.js @@ -3,7 +3,6 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; let Experiences = new Mongo.Collection('experiences'); @@ -50,18 +49,19 @@ Experiences.attachSchema(ExperienceSchema); const insertExperienceForCreature = function({experience, creatureId, userId}){ assertEditPermission(creatureId, userId); if (experience.xp){ - Creatures.update(creatureId, {$inc: { - 'denormalizedStats.xp': experience.xp - }}); + Creatures.update(creatureId, { + $inc: { 'denormalizedStats.xp': experience.xp }, + $set: { dirty: true }, + }); } if (experience.levels) { - Creatures.update(creatureId, {$inc: { - 'denormalizedStats.milestoneLevels': experience.levels - }}); + Creatures.update(creatureId, { + $inc: { 'denormalizedStats.milestoneLevels': experience.levels }, + $set: { dirty: true }, + }); } experience.creatureId = creatureId; let id = Experiences.insert(experience); - computeCreature(creatureId); return id; }; @@ -124,18 +124,19 @@ const removeExperience = new ValidatedMethod({ let creatureId = experience.creatureId assertEditPermission(creatureId, userId); if (experience.xp){ - Creatures.update(creatureId, {$inc: { - 'denormalizedStats.xp': -experience.xp - }}); + Creatures.update(creatureId, { + $inc: { 'denormalizedStats.xp': -experience.xp }, + $set: { dirty: true }, + }); } if (experience.levels) { - Creatures.update(creatureId, {$inc: { - 'denormalizedStats.milestoneLevels': -experience.levels - }}); + Creatures.update(creatureId, { + $inc: { 'denormalizedStats.milestoneLevels': -experience.levels }, + $set: { dirty: true }, + }); } experience.creatureId = creatureId; let numRemoved = Experiences.remove(experienceId); - computeCreature(creatureId); return numRemoved; }, }); @@ -173,9 +174,9 @@ const recomputeExperiences = new ValidatedMethod({ }); Creatures.update(creatureId, {$set: { 'denormalizedStats.xp': xp, - 'denormalizedStats.milestoneLevels': milestoneLevels + 'denormalizedStats.milestoneLevels': milestoneLevels, + dirty: true, }}); - computeCreature(creatureId); }, }); diff --git a/app/imports/api/engine/actions/doAction.js b/app/imports/api/engine/actions/doAction.js index e72e43bc..a53f628d 100644 --- a/app/imports/api/engine/actions/doAction.js +++ b/app/imports/api/engine/actions/doAction.js @@ -8,7 +8,6 @@ import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/ import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js'; import applyProperty from './applyProperty.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const doAction = new ValidatedMethod({ name: 'creatureProperties.doAction', @@ -77,9 +76,10 @@ const doAction = new ValidatedMethod({ doActionWork({creature, targets, properties, ancestors, method: this, methodScope: scope}); // Recompute all involved creatures - computeCreature(creature._id); - targets.forEach(target => { - computeCreature(target._id); + Creatures.update({ + _id: { $in: [creature._id, ...targetIds] } + }, { + dirty: true }); }, }); diff --git a/app/imports/api/engine/actions/doCastSpell.js b/app/imports/api/engine/actions/doCastSpell.js index 890c4879..8e0da001 100644 --- a/app/imports/api/engine/actions/doCastSpell.js +++ b/app/imports/api/engine/actions/doCastSpell.js @@ -7,7 +7,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; import { doActionWork } from '/imports/api/engine/actions/doAction.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import { CreatureLogSchema } from '/imports/api/creature/log/CreatureLogs.js'; const doAction = new ValidatedMethod({ @@ -129,12 +128,12 @@ const doAction = new ValidatedMethod({ } // Do the action - doActionWork({creature, targets, properties, ancestors, method: this, methodScope: scope, log}); - - // Recompute all involved creatures - computeCreature(creature._id); - targets.forEach(target => { - computeCreature(target._id); + doActionWork({ creature, targets, properties, ancestors, method: this, methodScope: scope, log }); + + Creatures.update({ + _id: { $in: [creature._id, ...targetIds] } + }, { + dirty: true }); }, }); diff --git a/app/imports/api/engine/actions/doCheck.js b/app/imports/api/engine/actions/doCheck.js index ce66db02..9f139f2c 100644 --- a/app/imports/api/engine/actions/doCheck.js +++ b/app/imports/api/engine/actions/doCheck.js @@ -5,7 +5,6 @@ import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/ge import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs.js'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import rollDice from '/imports/parser/rollDice.js'; import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; @@ -32,9 +31,6 @@ const doCheck = new ValidatedMethod({ // Do the check doCheckWork({creature, prop, method: this, methodScope: scope}); - - // Recompute all involved creatures - computeCreature(creature._id); }, }); diff --git a/app/imports/api/engine/computation/buildCreatureComputation.js b/app/imports/api/engine/computation/buildCreatureComputation.js index 47612830..0da96e21 100644 --- a/app/imports/api/engine/computation/buildCreatureComputation.js +++ b/app/imports/api/engine/computation/buildCreatureComputation.js @@ -42,7 +42,8 @@ function getProperties(creatureId) { if (loadedCreatures.has(creatureId)) { const creature = loadedCreatures.get(creatureId); const props = Array.from(creature.properties.values()); - return props; + const cloneProps = EJSON.clone(props); + return cloneProps } console.time(`Cache miss fetching from db: ${creatureId}`) const props = CreatureProperties.find({ @@ -56,10 +57,19 @@ function getProperties(creatureId) { return props; } -function getCreature(creatureId){ - return Creatures.findOne(creatureId, { +function getCreature(creatureId) { + if (loadedCreatures.has(creatureId)) { + const loadedCreature = loadedCreatures.get(creatureId); + const creature = loadedCreature.creatures.get(creatureId); + if (creature) return creature; + } + console.time(`Cache miss on Creature: ${creatureId}`); + const creature = Creatures.findOne(creatureId, { denormalizedStats: 1, + variables: 1, }); + console.timeEnd(`Cache miss on Creature: ${creatureId}`); + return creature; } export function buildComputationFromProps(properties, creature){ @@ -91,6 +101,8 @@ export function buildComputationFromProps(properties, creature){ // Process the properties one by one properties.forEach(prop => { + // The prop has been processed, it's no longer dirty + delete prop.dirty; const computedSchema = computedOnlySchemas[prop.type]; removeSchemaFields([computedSchema, denormSchema], prop); diff --git a/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js b/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js index ccf8246e..fcad024c 100644 --- a/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js +++ b/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js @@ -21,6 +21,7 @@ export default function writeAlteredProperties(computation){ 'deactivatedByAncestor', 'deactivatedByToggle', 'damage', + 'dirty', ...schema.objectKeys(), ]; op = addChangedKeysToOp(op, keys, original, changed); diff --git a/app/imports/api/engine/computation/writeComputation/writeScope.js b/app/imports/api/engine/computation/writeComputation/writeScope.js index c318fb9e..abee4290 100644 --- a/app/imports/api/engine/computation/writeComputation/writeScope.js +++ b/app/imports/api/engine/computation/writeComputation/writeScope.js @@ -20,7 +20,6 @@ export default function writeScope(creatureId, computation) { // Only update changed fields if (!EJSON.equals(variables[key], scope[key])) { if (!$set) $set = {}; - // Set the changed key in the creature variables $set[`variables.${key}`] = scope[key]; } diff --git a/app/imports/api/engine/computeCreature.js b/app/imports/api/engine/computeCreature.js index 75064776..7bc1a953 100644 --- a/app/imports/api/engine/computeCreature.js +++ b/app/imports/api/engine/computeCreature.js @@ -6,6 +6,7 @@ import writeErrors from './computation/writeComputation/writeErrors.js'; export default function computeCreature(creatureId){ if (Meteor.isClient) return; + console.log('compute') const computation = buildCreatureComputation(creatureId); computeComputation(computation, creatureId); } diff --git a/app/imports/api/engine/loadCreatures.js b/app/imports/api/engine/loadCreatures.js index f7870440..0a60250b 100644 --- a/app/imports/api/engine/loadCreatures.js +++ b/app/imports/api/engine/loadCreatures.js @@ -1,6 +1,7 @@ +import { debounce } from 'lodash'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; - +import computeCreature from './computeCreature'; export const loadedCreatures = new Map(); // creatureId => {creature, properties, etc.} export function loadCreature(creatureId, subscription) { @@ -35,27 +36,33 @@ class LoadedCreature { // the required documents const self = this; Tracker.nonreactive(() => { - self.subs = new Set([sub]); + const compute = debounce(Meteor.bindEnvironment(() => { + computeCreature(creatureId); + }), 100); + self.properties = new Map(); // Observe all creature properties which are needed for computation self.propertyObserver = CreatureProperties.find({ 'ancestors.id': creatureId, removed: { $ne: true }, }, { - // sort: { order: 1 }, + sort: { order: 1 }, fields: { icon: 0 }, }).observeChanges({ added(id, fields) { fields._id = id; - return self.addProperty(fields) + self.addProperty(fields); + if (fields.dirty) compute(); }, changed(id, fields) { - return self.changeProperty(id, fields); + self.changeProperty(id, fields); + if (fields.dirty) compute(); }, removed(id) { - return self.removeProperty(id); + self.removeProperty(id); + compute(); }, }); @@ -67,12 +74,14 @@ class LoadedCreature { added(id, fields) { fields._id = id; self.addCreature(fields) + if (fields.dirty) compute(); }, changed(id, fields) { - return self.changeCreature(id, fields); + self.changeCreature(id, fields); + if (fields.dirty) compute(); }, removed(id) { - return self.removeCreature(id); + self.removeCreature(id); }, }); diff --git a/app/imports/api/parenting/organizeMethods.js b/app/imports/api/parenting/organizeMethods.js index f104154c..c4b822b2 100644 --- a/app/imports/api/parenting/organizeMethods.js +++ b/app/imports/api/parenting/organizeMethods.js @@ -8,7 +8,7 @@ import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; import getCollectionByName from '/imports/api/parenting/getCollectionByName.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; +import Creatures from '/imports/api/creature/creatures/Creatures.js'; const organizeDoc = new ValidatedMethod({ name: 'organize.organizeDoc', @@ -57,10 +57,11 @@ const organizeDoc = new ValidatedMethod({ let parentCreatures = getCreatureAncestors(parent); if (!skipRecompute){ let creaturesToRecompute = union(docCreatures, parentCreatures); - // Recompute the creatures - creaturesToRecompute.forEach(id => { - // Some Dependencies depend on ancestry, so a full recompute is needed - computeCreature(id); + // Mark the creatures for recompute + Creatures.update({ + _id: { $in: creaturesToRecompute } + }, { + dirty: true }); } }, @@ -85,9 +86,14 @@ const reorderDoc = new ValidatedMethod({ assertDocEditPermission(doc, this.userId); safeUpdateDocOrder({docRef, order}); // Recompute the affected creatures - getCreatureAncestors(doc).forEach(id => { - computeCreature(id); - }); + const ancestors = getCreatureAncestors(doc); + if (ancestors.length) { + Creatures.update({ + _id: { $in: ancestors } + }, { + dirty: true + }); + } }, }); diff --git a/app/imports/api/parenting/softRemove.js b/app/imports/api/parenting/softRemove.js index 9ef16f8c..099abfc2 100644 --- a/app/imports/api/parenting/softRemove.js +++ b/app/imports/api/parenting/softRemove.js @@ -40,17 +40,22 @@ const restoreError = function(){ ); }; -export function restore({_id, collection}){ +export function restore({ _id, collection, extraUpdates}){ if (typeof collection === 'string') { collection = getCollectionByName(collection); } + const update = { + $unset: { + removed: 1, + removedAt: 1, + }, + ...extraUpdates + } + let numUpdated = collection.update({ _id, removedWith: {$exists: false} - }, { $unset: { - removed: 1, - removedAt: 1, - }}, { + }, update , { selector: {type: 'any'}, },); if (numUpdated === 0) restoreError();