Also removed dependency group calculation because the optimisation isn't as useful to reduce DB calls if the creature is in memory anyway
120 lines
3.5 KiB
JavaScript
120 lines
3.5 KiB
JavaScript
import { Meteor } from 'meteor/meteor'
|
|
import { EJSON } from 'meteor/ejson';
|
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
|
import propertySchemasIndex from '/imports/api/properties/computedOnlyPropertySchemasIndex.js';
|
|
|
|
export default function writeAlteredProperties(computation){
|
|
let bulkWriteOperations = [];
|
|
// Loop through all properties on the memo
|
|
computation.props.forEach(changed => {
|
|
let schema = propertySchemasIndex[changed.type];
|
|
if (!schema){
|
|
console.warn('No schema for ' + changed.type);
|
|
return;
|
|
}
|
|
let id = changed._id;
|
|
let op = undefined;
|
|
let original = computation.originalPropsById[id];
|
|
let keys = [
|
|
'inactive',
|
|
'deactivatedBySelf',
|
|
'deactivatedByAncestor',
|
|
'deactivatedByToggle',
|
|
'damage',
|
|
...schema.objectKeys(),
|
|
];
|
|
op = addChangedKeysToOp(op, keys, original, changed);
|
|
if (op){
|
|
bulkWriteOperations.push(op);
|
|
}
|
|
});
|
|
bulkWriteProperties(bulkWriteOperations);
|
|
//if (bulkWriteOperations.length) console.log(`Wrote ${bulkWriteOperations.length} props`);
|
|
}
|
|
|
|
function addChangedKeysToOp(op, keys, original, changed) {
|
|
// Loop through all keys that can be changed by computation
|
|
// and compile an operation that sets all those keys
|
|
for (let key of keys) {
|
|
if (!EJSON.equals(original[key], changed[key])){
|
|
if (!op) op = newOperation(original._id, changed.type);
|
|
let value = changed[key];
|
|
if (value === undefined){
|
|
// Unset values that become undefined
|
|
addUnsetOp(op, key);
|
|
} else {
|
|
// Set values that changed to something else
|
|
addSetOp(op, key, value);
|
|
}
|
|
}
|
|
}
|
|
return op;
|
|
}
|
|
|
|
function newOperation(_id, type){
|
|
let newOp = {
|
|
updateOne: {
|
|
filter: {_id},
|
|
update: {},
|
|
}
|
|
};
|
|
if (Meteor.isClient){
|
|
newOp.type = type;
|
|
}
|
|
return newOp;
|
|
}
|
|
|
|
function addSetOp(op, key, value){
|
|
if (op.updateOne.update.$set){
|
|
op.updateOne.update.$set[key] = value;
|
|
} else {
|
|
op.updateOne.update.$set = {[key]: value};
|
|
}
|
|
}
|
|
|
|
function addUnsetOp(op, key){
|
|
if (op.updateOne.update.$unset){
|
|
op.updateOne.update.$unset[key] = 1;
|
|
} else {
|
|
op.updateOne.update.$unset = {[key]: 1};
|
|
}
|
|
}
|
|
|
|
// If we re-enable client-side sheet recalculation, this needs to be run on
|
|
// both client and server to preserve latency compensation. Bulkwrite breaks
|
|
// latency compensation and causes flickering
|
|
function writePropertiesSequentially(bulkWriteOps) {
|
|
bulkWriteOps.forEach(op => {
|
|
let updateOneOrMany = op.updateOne || op.updateMany;
|
|
CreatureProperties.update(updateOneOrMany.filter, updateOneOrMany.update, {
|
|
// The bulk code is bypassing validation, so do the same here
|
|
// selector: {type: op.type} // include this if bypass is off
|
|
bypassCollection2: true,
|
|
});
|
|
});
|
|
//if (bulkWriteOps.length) console.log(`Wrote ${bulkWriteOps.length} props`);
|
|
}
|
|
|
|
// This is more efficient on the database, but significantly less efficient
|
|
// in the UI because of incompatibility with latency compensation. If the
|
|
// duplicate redraws can be fixed, this is a strictly better way of processing
|
|
// writes
|
|
function bulkWriteProperties(bulkWriteOps){
|
|
if (!bulkWriteOps.length) return;
|
|
// bulkWrite is only available on the server
|
|
if (Meteor.isServer) {
|
|
CreatureProperties.rawCollection().bulkWrite(
|
|
bulkWriteOps,
|
|
{ordered : false},
|
|
function(e){
|
|
if (e) {
|
|
console.error('Bulk write failed: ');
|
|
console.error(e);
|
|
}
|
|
}
|
|
);
|
|
} else {
|
|
writePropertiesSequentially(bulkWriteOps);
|
|
}
|
|
}
|