Replaced manual recompute calls with dirty flag settings

This commit is contained in:
Stefan Zermatten
2022-05-11 15:42:29 +02:00
parent 7a35c66904
commit 1b3efae81a
28 changed files with 138 additions and 128 deletions

View File

@@ -82,6 +82,13 @@ const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({
index: 1, index: 1,
removeBeforeCompute: true, 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); CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema);

View File

@@ -4,7 +4,6 @@ import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
const adjustQuantity = new ValidatedMethod({ const adjustQuantity = new ValidatedMethod({
name: 'creatureProperties.adjustQuantity', name: 'creatureProperties.adjustQuantity',
@@ -29,10 +28,6 @@ const adjustQuantity = new ValidatedMethod({
// Do work // Do work
adjustQuantityWork({property, operation, value}); 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'){ if (operation === 'set'){
CreatureProperties.update(property._id, { CreatureProperties.update(property._id, {
$set: {quantity: value} $set: {quantity: value, dirty: true}
}, { }, {
selector: property selector: property
}); });
@@ -57,7 +52,8 @@ export function adjustQuantityWork({property, operation, value}){
let currentQuantity = property.quantity; let currentQuantity = property.quantity;
if (currentQuantity + value < 0) value = -currentQuantity; if (currentQuantity + value < 0) value = -currentQuantity;
CreatureProperties.update(property._id, { CreatureProperties.update(property._id, {
$inc: {quantity: value} $inc: { quantity: value },
$set: { dirty: true }
}, { }, {
selector: property selector: property
}); });

View File

@@ -4,7 +4,6 @@ import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
const damageProperty = new ValidatedMethod({ const damageProperty = new ValidatedMethod({
name: 'creatureProperties.damage', name: 'creatureProperties.damage',
@@ -38,7 +37,6 @@ const damageProperty = new ValidatedMethod({
); );
} }
let result = damagePropertyWork({ property, operation, value }); let result = damagePropertyWork({ property, operation, value });
computeCreature(rootCreature._id);
return result; return result;
}, },
}); });
@@ -69,7 +67,7 @@ export function damagePropertyWork({property, operation, value}){
// Write the results // Write the results
CreatureProperties.update(property._id, { CreatureProperties.update(property._id, {
$set: {damage, value: newValue} $set: {damage, value: newValue, dirty: true}
}, { }, {
selector: property selector: property
}); });

View File

@@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur
import Creatures from '/imports/api/creature/creatures/Creatures.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
const dealDamage = new ValidatedMethod({ const dealDamage = new ValidatedMethod({
name: 'creatureProperties.dealDamage', name: 'creatureProperties.dealDamage',
@@ -33,7 +32,6 @@ const dealDamage = new ValidatedMethod({
assertEditPermission(creature, this.userId); assertEditPermission(creature, this.userId);
const totalDamage = dealDamageWork({creature, damageType, amount}) const totalDamage = dealDamageWork({creature, damageType, amount})
computeCreature(creatureId);
return totalDamage; return totalDamage;
}, },
}); });

View File

@@ -9,7 +9,6 @@ import {
renewDocIds renewDocIds
} from '/imports/api/parenting/parenting.js'; } from '/imports/api/parenting/parenting.js';
import { reorderDocs } from '/imports/api/parenting/order.js'; import { reorderDocs } from '/imports/api/parenting/order.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
var snackbar; var snackbar;
if (Meteor.isClient){ if (Meteor.isClient){
snackbar = require( snackbar = require(
@@ -77,6 +76,9 @@ const duplicateProperty = new ValidatedMethod({
// Order the root node // Order the root node
property.order += 0.5; property.order += 0.5;
// Mark the sheet as needing recompute
property.dirty = true;
// Insert the properties // Insert the properties
CreatureProperties.batchInsert([property, ...nodes]); CreatureProperties.batchInsert([property, ...nodes]);
@@ -87,9 +89,6 @@ const duplicateProperty = new ValidatedMethod({
ancestorId: property.ancestors[0].id, ancestorId: property.ancestors[0].id,
}); });
// Inserting a creature property invalidates dependencies: full recompute
computeCreature(creature._id);
return propertyId; return propertyId;
}, },
}); });

View File

@@ -4,7 +4,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { organizeDoc } from '/imports/api/parenting/organizeMethods.js'; import { organizeDoc } from '/imports/api/parenting/organizeMethods.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.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 BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js'; import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
@@ -29,7 +28,7 @@ const equipItem = new ValidatedMethod({
let creature = getRootCreatureAncestor(item); let creature = getRootCreatureAncestor(item);
assertEditPermission(creature, this.userId); assertEditPermission(creature, this.userId);
CreatureProperties.update(_id, { CreatureProperties.update(_id, {
$set: {equipped}, $set: { equipped, dirty: true },
}, { }, {
selector: {type: 'item'}, selector: {type: 'item'},
}); });
@@ -46,8 +45,6 @@ const equipItem = new ValidatedMethod({
order: Number.MAX_SAFE_INTEGER, order: Number.MAX_SAFE_INTEGER,
skipRecompute: true, skipRecompute: true,
}); });
computeCreature(creature._id);
}, },
}); });

View File

@@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
const flipToggle = new ValidatedMethod({ const flipToggle = new ValidatedMethod({
name: 'creatureProperties.flipToggle', name: 'creatureProperties.flipToggle',
@@ -36,12 +35,10 @@ const flipToggle = new ValidatedMethod({
CreatureProperties.update(_id, {$set: { CreatureProperties.update(_id, {$set: {
enabled: !currentValue, enabled: !currentValue,
disabled: currentValue, disabled: currentValue,
dirty: true,
}}, { }}, {
selector: {type: 'toggle'}, selector: {type: 'toggle'},
}); });
// Updating a toggle is likely to change the whole tree, do a full recompute
computeCreature(rootCreature._id);
}, },
}); });

View File

@@ -5,7 +5,6 @@ import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/ge
import SimpleSchema from 'simpl-schema'; import SimpleSchema from 'simpl-schema';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { reorderDocs } from '/imports/api/parenting/order.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 { getAncestry } from '/imports/api/parenting/parenting.js';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js'; import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
@@ -132,14 +131,13 @@ const insertPropertyAsChildOfTag = new ValidatedMethod({
export function insertPropertyWork({property, creature}){ export function insertPropertyWork({property, creature}){
delete property._id; delete property._id;
property.dirty = true;
let _id = CreatureProperties.insert(property); let _id = CreatureProperties.insert(property);
// Tree structure changed by insert, reorder the tree // Tree structure changed by insert, reorder the tree
reorderDocs({ reorderDocs({
collection: CreatureProperties, collection: CreatureProperties,
ancestorId: creature._id, ancestorId: creature._id,
}); });
// Inserting a creature property invalidates dependencies: full recompute
computeCreature(creature._id);
return _id; return _id;
} }

View File

@@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur
import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.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 { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { import {
setLineageOfDocs, setLineageOfDocs,
@@ -71,9 +70,6 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
collection: CreatureProperties, collection: CreatureProperties,
ancestorId: rootCreature._id, 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 the docId of the last property, the inserted root property
return rootId; return rootId;
}, },
@@ -135,6 +131,9 @@ function insertPropertyFromNode(nodeId, ancestors, order){
node.order = order; node.order = order;
} }
// Mark root as dirty
node.dirty = true;
// Insert the creature properties // Insert the creature properties
CreatureProperties.batchInsert(nodes); CreatureProperties.batchInsert(nodes);
return node; return node;

View File

@@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
const pullFromProperty = new ValidatedMethod({ const pullFromProperty = new ValidatedMethod({
name: 'creatureProperties.pull', name: 'creatureProperties.pull',
@@ -21,15 +20,12 @@ const pullFromProperty = new ValidatedMethod({
// Do work // Do work
CreatureProperties.update(_id, { CreatureProperties.update(_id, {
$pull: {[path.join('.')]: {_id: itemId}}, $pull: { [path.join('.')]: { _id: itemId } },
$set: { dirty: true }
}, { }, {
selector: {type: property.type}, selector: {type: property.type},
getAutoValues: false, getAutoValues: false,
}); });
// TODO figure out if this method can change deps or not
computeCreature(rootCreature._id);
// recomputePropertyDependencies(property);
} }
}); });

View File

@@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
import { get } from 'lodash'; import { get } from 'lodash';
const pushToProperty = new ValidatedMethod({ const pushToProperty = new ValidatedMethod({
@@ -39,13 +38,11 @@ const pushToProperty = new ValidatedMethod({
// Do work // Do work
CreatureProperties.update(_id, { CreatureProperties.update(_id, {
$push: {[joinedPath]: value}, $push: { [joinedPath]: value },
$set: { dirty: true },
}, { }, {
selector: {type: property.type}, selector: {type: property.type},
}); });
// TODO figure out if this method can change deps or not
computeCreature(rootCreature._id);
} }
}); });

View File

@@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { restore } from '/imports/api/parenting/softRemove.js'; import { restore } from '/imports/api/parenting/softRemove.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
const restoreProperty = new ValidatedMethod({ const restoreProperty = new ValidatedMethod({
name: 'creatureProperties.restore', name: 'creatureProperties.restore',
@@ -24,10 +23,13 @@ const restoreProperty = new ValidatedMethod({
assertEditPermission(rootCreature, this.userId); assertEditPermission(rootCreature, this.userId);
// Do work // Do work
restore({_id, collection: CreatureProperties}); restore({
_id,
// Changes dependency tree by restoring children collection: CreatureProperties,
computeCreature(rootCreature._id); extraUpdates: {
$set: { dirty: true }
},
});
} }
}); });

View File

@@ -4,7 +4,6 @@ import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
const selectAmmoItem = new ValidatedMethod({ const selectAmmoItem = new ValidatedMethod({
name: 'creatureProperties.selectAmmoItem', name: 'creatureProperties.selectAmmoItem',
@@ -37,15 +36,10 @@ const selectAmmoItem = new ValidatedMethod({
} }
let path = `resources.itemsConsumed.${itemConsumedIndex}.itemId`; let path = `resources.itemsConsumed.${itemConsumedIndex}.itemId`;
CreatureProperties.update(actionId, { CreatureProperties.update(actionId, {
$set: {[path]: itemId} $set: { [path]: itemId, dirty: true }
}, { }, {
selector: action, 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);
}, },
}); });

View File

@@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { softRemove } from '/imports/api/parenting/softRemove.js'; import { softRemove } from '/imports/api/parenting/softRemove.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
const softRemoveProperty = new ValidatedMethod({ const softRemoveProperty = new ValidatedMethod({
name: 'creatureProperties.softRemove', name: 'creatureProperties.softRemove',
@@ -25,9 +24,6 @@ const softRemoveProperty = new ValidatedMethod({
// Do work // Do work
softRemove({_id, collection: CreatureProperties}); softRemove({_id, collection: CreatureProperties});
// Changes dependency tree by removing children
computeCreature(rootCreature._id);
} }
}); });

View File

@@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
const updateCreatureProperty = new ValidatedMethod({ const updateCreatureProperty = new ValidatedMethod({
name: 'creatureProperties.update', name: 'creatureProperties.update',
@@ -37,17 +36,13 @@ const updateCreatureProperty = new ValidatedMethod({
let modifier; let modifier;
// unset empty values // unset empty values
if (value === null || value === undefined){ if (value === null || value === undefined){
modifier = {$unset: {[pathString]: 1}}; modifier = { $unset: {[pathString]: 1}, $set: { dirty: true } };
} else { } else {
modifier = {$set: {[pathString]: value}}; modifier = { $set: {[pathString]: value, dirty: true } };
} }
CreatureProperties.update(_id, modifier, { CreatureProperties.update(_id, modifier, {
selector: {type: property.type}, 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);
}, },
}); });

View File

@@ -100,6 +100,11 @@ let CreatureSchema = new SimpleSchema({
type: SimpleSchema.Integer, type: SimpleSchema.Integer,
defaultValue: 0, 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 // Version of computation engine that was last used to compute this creature
computeVersion: { computeVersion: {
type: String, type: String,

View File

@@ -4,10 +4,9 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import Creatures from '/imports/api/creature/creatures/Creatures.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
const restCreature = new ValidatedMethod({ const restCreature = new ValidatedMethod({
name: 'creature.methods.longRest', name: 'creature.methods.rest',
validate: new SimpleSchema({ validate: new SimpleSchema({
creatureId: { creatureId: {
type: String, type: String,
@@ -51,7 +50,10 @@ const restCreature = new ValidatedMethod({
// update all attribute's damage // update all attribute's damage
filter.type = 'attribute'; filter.type = 'attribute';
CreatureProperties.update(filter, { CreatureProperties.update(filter, {
$set: {damage: 0} $set: {
damage: 0,
dirty: true,
}
}, { }, {
selector: {type: 'attribute'}, selector: {type: 'attribute'},
multi: true, multi: true,
@@ -63,7 +65,10 @@ const restCreature = new ValidatedMethod({
'spell' 'spell'
]}; ]};
CreatureProperties.update(filter, { CreatureProperties.update(filter, {
$set: {usesUsed: 0} $set: {
usesUsed: 0,
dirty: true,
}
}, { }, {
selector: {type: 'action'}, selector: {type: 'action'},
multi: true, multi: true,
@@ -103,13 +108,15 @@ const restCreature = new ValidatedMethod({
recoverableHd -= amountToRecover; recoverableHd -= amountToRecover;
resultingDamage = hd.damage - amountToRecover; resultingDamage = hd.damage - amountToRecover;
CreatureProperties.update(hd._id, { CreatureProperties.update(hd._id, {
$set: {damage: resultingDamage} $set: {
damage: resultingDamage,
dirty: true,
}
}, { }, {
selector: {type: 'attribute'}, selector: {type: 'attribute'},
}); });
}); });
} }
computeCreature(creatureId);
}, },
}); });

View File

@@ -3,7 +3,6 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import Creatures from '/imports/api/creature/creatures/Creatures.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'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
let Experiences = new Mongo.Collection('experiences'); let Experiences = new Mongo.Collection('experiences');
@@ -50,18 +49,19 @@ Experiences.attachSchema(ExperienceSchema);
const insertExperienceForCreature = function({experience, creatureId, userId}){ const insertExperienceForCreature = function({experience, creatureId, userId}){
assertEditPermission(creatureId, userId); assertEditPermission(creatureId, userId);
if (experience.xp){ if (experience.xp){
Creatures.update(creatureId, {$inc: { Creatures.update(creatureId, {
'denormalizedStats.xp': experience.xp $inc: { 'denormalizedStats.xp': experience.xp },
}}); $set: { dirty: true },
});
} }
if (experience.levels) { if (experience.levels) {
Creatures.update(creatureId, {$inc: { Creatures.update(creatureId, {
'denormalizedStats.milestoneLevels': experience.levels $inc: { 'denormalizedStats.milestoneLevels': experience.levels },
}}); $set: { dirty: true },
});
} }
experience.creatureId = creatureId; experience.creatureId = creatureId;
let id = Experiences.insert(experience); let id = Experiences.insert(experience);
computeCreature(creatureId);
return id; return id;
}; };
@@ -124,18 +124,19 @@ const removeExperience = new ValidatedMethod({
let creatureId = experience.creatureId let creatureId = experience.creatureId
assertEditPermission(creatureId, userId); assertEditPermission(creatureId, userId);
if (experience.xp){ if (experience.xp){
Creatures.update(creatureId, {$inc: { Creatures.update(creatureId, {
'denormalizedStats.xp': -experience.xp $inc: { 'denormalizedStats.xp': -experience.xp },
}}); $set: { dirty: true },
});
} }
if (experience.levels) { if (experience.levels) {
Creatures.update(creatureId, {$inc: { Creatures.update(creatureId, {
'denormalizedStats.milestoneLevels': -experience.levels $inc: { 'denormalizedStats.milestoneLevels': -experience.levels },
}}); $set: { dirty: true },
});
} }
experience.creatureId = creatureId; experience.creatureId = creatureId;
let numRemoved = Experiences.remove(experienceId); let numRemoved = Experiences.remove(experienceId);
computeCreature(creatureId);
return numRemoved; return numRemoved;
}, },
}); });
@@ -173,9 +174,9 @@ const recomputeExperiences = new ValidatedMethod({
}); });
Creatures.update(creatureId, {$set: { Creatures.update(creatureId, {$set: {
'denormalizedStats.xp': xp, 'denormalizedStats.xp': xp,
'denormalizedStats.milestoneLevels': milestoneLevels 'denormalizedStats.milestoneLevels': milestoneLevels,
dirty: true,
}}); }});
computeCreature(creatureId);
}, },
}); });

View File

@@ -8,7 +8,6 @@ import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js'; import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
import applyProperty from './applyProperty.js'; import applyProperty from './applyProperty.js';
import computeCreature from '/imports/api/engine/computeCreature.js';
const doAction = new ValidatedMethod({ const doAction = new ValidatedMethod({
name: 'creatureProperties.doAction', name: 'creatureProperties.doAction',
@@ -77,9 +76,10 @@ const doAction = new ValidatedMethod({
doActionWork({creature, targets, properties, ancestors, method: this, methodScope: scope}); doActionWork({creature, targets, properties, ancestors, method: this, methodScope: scope});
// Recompute all involved creatures // Recompute all involved creatures
computeCreature(creature._id); Creatures.update({
targets.forEach(target => { _id: { $in: [creature._id, ...targetIds] }
computeCreature(target._id); }, {
dirty: true
}); });
}, },
}); });

View File

@@ -7,7 +7,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
import { doActionWork } from '/imports/api/engine/actions/doAction.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'; import { CreatureLogSchema } from '/imports/api/creature/log/CreatureLogs.js';
const doAction = new ValidatedMethod({ const doAction = new ValidatedMethod({
@@ -129,12 +128,12 @@ const doAction = new ValidatedMethod({
} }
// Do the action // Do the action
doActionWork({creature, targets, properties, ancestors, method: this, methodScope: scope, log}); doActionWork({ creature, targets, properties, ancestors, method: this, methodScope: scope, log });
// Recompute all involved creatures Creatures.update({
computeCreature(creature._id); _id: { $in: [creature._id, ...targetIds] }
targets.forEach(target => { }, {
computeCreature(target._id); dirty: true
}); });
}, },
}); });

View File

@@ -5,7 +5,6 @@ import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/ge
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs.js'; import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs.js';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.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 rollDice from '/imports/parser/rollDice.js';
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
@@ -32,9 +31,6 @@ const doCheck = new ValidatedMethod({
// Do the check // Do the check
doCheckWork({creature, prop, method: this, methodScope: scope}); doCheckWork({creature, prop, method: this, methodScope: scope});
// Recompute all involved creatures
computeCreature(creature._id);
}, },
}); });

View File

@@ -42,7 +42,8 @@ function getProperties(creatureId) {
if (loadedCreatures.has(creatureId)) { if (loadedCreatures.has(creatureId)) {
const creature = loadedCreatures.get(creatureId); const creature = loadedCreatures.get(creatureId);
const props = Array.from(creature.properties.values()); const props = Array.from(creature.properties.values());
return props; const cloneProps = EJSON.clone(props);
return cloneProps
} }
console.time(`Cache miss fetching from db: ${creatureId}`) console.time(`Cache miss fetching from db: ${creatureId}`)
const props = CreatureProperties.find({ const props = CreatureProperties.find({
@@ -56,10 +57,19 @@ function getProperties(creatureId) {
return props; return props;
} }
function getCreature(creatureId){ function getCreature(creatureId) {
return Creatures.findOne(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, denormalizedStats: 1,
variables: 1,
}); });
console.timeEnd(`Cache miss on Creature: ${creatureId}`);
return creature;
} }
export function buildComputationFromProps(properties, creature){ export function buildComputationFromProps(properties, creature){
@@ -91,6 +101,8 @@ export function buildComputationFromProps(properties, creature){
// Process the properties one by one // Process the properties one by one
properties.forEach(prop => { properties.forEach(prop => {
// The prop has been processed, it's no longer dirty
delete prop.dirty;
const computedSchema = computedOnlySchemas[prop.type]; const computedSchema = computedOnlySchemas[prop.type];
removeSchemaFields([computedSchema, denormSchema], prop); removeSchemaFields([computedSchema, denormSchema], prop);

View File

@@ -21,6 +21,7 @@ export default function writeAlteredProperties(computation){
'deactivatedByAncestor', 'deactivatedByAncestor',
'deactivatedByToggle', 'deactivatedByToggle',
'damage', 'damage',
'dirty',
...schema.objectKeys(), ...schema.objectKeys(),
]; ];
op = addChangedKeysToOp(op, keys, original, changed); op = addChangedKeysToOp(op, keys, original, changed);

View File

@@ -20,7 +20,6 @@ export default function writeScope(creatureId, computation) {
// Only update changed fields // Only update changed fields
if (!EJSON.equals(variables[key], scope[key])) { if (!EJSON.equals(variables[key], scope[key])) {
if (!$set) $set = {}; if (!$set) $set = {};
// Set the changed key in the creature variables // Set the changed key in the creature variables
$set[`variables.${key}`] = scope[key]; $set[`variables.${key}`] = scope[key];
} }

View File

@@ -6,6 +6,7 @@ import writeErrors from './computation/writeComputation/writeErrors.js';
export default function computeCreature(creatureId){ export default function computeCreature(creatureId){
if (Meteor.isClient) return; if (Meteor.isClient) return;
console.log('compute')
const computation = buildCreatureComputation(creatureId); const computation = buildCreatureComputation(creatureId);
computeComputation(computation, creatureId); computeComputation(computation, creatureId);
} }

View File

@@ -1,6 +1,7 @@
import { debounce } from 'lodash';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js';
import computeCreature from './computeCreature';
export const loadedCreatures = new Map(); // creatureId => {creature, properties, etc.} export const loadedCreatures = new Map(); // creatureId => {creature, properties, etc.}
export function loadCreature(creatureId, subscription) { export function loadCreature(creatureId, subscription) {
@@ -35,27 +36,33 @@ class LoadedCreature {
// the required documents // the required documents
const self = this; const self = this;
Tracker.nonreactive(() => { Tracker.nonreactive(() => {
self.subs = new Set([sub]); self.subs = new Set([sub]);
const compute = debounce(Meteor.bindEnvironment(() => {
computeCreature(creatureId);
}), 100);
self.properties = new Map(); self.properties = new Map();
// Observe all creature properties which are needed for computation // Observe all creature properties which are needed for computation
self.propertyObserver = CreatureProperties.find({ self.propertyObserver = CreatureProperties.find({
'ancestors.id': creatureId, 'ancestors.id': creatureId,
removed: { $ne: true }, removed: { $ne: true },
}, { }, {
// sort: { order: 1 }, sort: { order: 1 },
fields: { icon: 0 }, fields: { icon: 0 },
}).observeChanges({ }).observeChanges({
added(id, fields) { added(id, fields) {
fields._id = id; fields._id = id;
return self.addProperty(fields) self.addProperty(fields);
if (fields.dirty) compute();
}, },
changed(id, fields) { changed(id, fields) {
return self.changeProperty(id, fields); self.changeProperty(id, fields);
if (fields.dirty) compute();
}, },
removed(id) { removed(id) {
return self.removeProperty(id); self.removeProperty(id);
compute();
}, },
}); });
@@ -67,12 +74,14 @@ class LoadedCreature {
added(id, fields) { added(id, fields) {
fields._id = id; fields._id = id;
self.addCreature(fields) self.addCreature(fields)
if (fields.dirty) compute();
}, },
changed(id, fields) { changed(id, fields) {
return self.changeCreature(id, fields); self.changeCreature(id, fields);
if (fields.dirty) compute();
}, },
removed(id) { removed(id) {
return self.removeCreature(id); self.removeCreature(id);
}, },
}); });

View File

@@ -8,7 +8,7 @@ import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
import getCollectionByName from '/imports/api/parenting/getCollectionByName.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({ const organizeDoc = new ValidatedMethod({
name: 'organize.organizeDoc', name: 'organize.organizeDoc',
@@ -57,10 +57,11 @@ const organizeDoc = new ValidatedMethod({
let parentCreatures = getCreatureAncestors(parent); let parentCreatures = getCreatureAncestors(parent);
if (!skipRecompute){ if (!skipRecompute){
let creaturesToRecompute = union(docCreatures, parentCreatures); let creaturesToRecompute = union(docCreatures, parentCreatures);
// Recompute the creatures // Mark the creatures for recompute
creaturesToRecompute.forEach(id => { Creatures.update({
// Some Dependencies depend on ancestry, so a full recompute is needed _id: { $in: creaturesToRecompute }
computeCreature(id); }, {
dirty: true
}); });
} }
}, },
@@ -85,9 +86,14 @@ const reorderDoc = new ValidatedMethod({
assertDocEditPermission(doc, this.userId); assertDocEditPermission(doc, this.userId);
safeUpdateDocOrder({docRef, order}); safeUpdateDocOrder({docRef, order});
// Recompute the affected creatures // Recompute the affected creatures
getCreatureAncestors(doc).forEach(id => { const ancestors = getCreatureAncestors(doc);
computeCreature(id); if (ancestors.length) {
}); Creatures.update({
_id: { $in: ancestors }
}, {
dirty: true
});
}
}, },
}); });

View File

@@ -40,17 +40,22 @@ const restoreError = function(){
); );
}; };
export function restore({_id, collection}){ export function restore({ _id, collection, extraUpdates}){
if (typeof collection === 'string') { if (typeof collection === 'string') {
collection = getCollectionByName(collection); collection = getCollectionByName(collection);
} }
const update = {
$unset: {
removed: 1,
removedAt: 1,
},
...extraUpdates
}
let numUpdated = collection.update({ let numUpdated = collection.update({
_id, _id,
removedWith: {$exists: false} removedWith: {$exists: false}
}, { $unset: { }, update , {
removed: 1,
removedAt: 1,
}}, {
selector: {type: 'any'}, selector: {type: 'any'},
},); },);
if (numUpdated === 0) restoreError(); if (numUpdated === 0) restoreError();