Fixed error where dependency loops including classLevels break the sheet

This commit is contained in:
Stefan Zermatten
2022-02-26 13:06:00 +02:00
parent 59c69a46a8
commit 7ee4a22d77
9 changed files with 57 additions and 7 deletions

View File

@@ -120,6 +120,20 @@ let CreatureSchema = new SimpleSchema({
blackbox: true, blackbox: true,
defaultValue: {} defaultValue: {}
}, },
computeErrors: {
type: Array,
optional: true,
},
'computeErrors.$': {
type: Object,
},
'computeErrors.$.type': {
type: String,
},
'computeErrors.$.details' : {
type: Object,
blackbox: true,
},
// Tabletop // Tabletop
tabletop: { tabletop: {

View File

@@ -10,6 +10,7 @@ export default class CreatureComputation {
this.scope = {}; this.scope = {};
this.props = properties; this.props = properties;
this.dependencyGraph = createGraph(); this.dependencyGraph = createGraph();
this.errors = [];
// Store properties for easy access later // Store properties for easy access later
properties.forEach(prop => { properties.forEach(prop => {

View File

@@ -1,6 +1,6 @@
export default function aggregateClassLevel({node, linkedNode, link}){ export default function aggregateClassLevel({node, linkedNode, link}){
if (node.data.inactive) return;
if (link.data === 'classLevel'){ if (link.data === 'classLevel'){
if (node.data.inactive) return;
if (!node.data.classLevelAggregator) node.data.classLevelAggregator = { if (!node.data.classLevelAggregator) node.data.classLevelAggregator = {
levelsFilled: [true], // Level 0 is always filled levelsFilled: [true], // Level 0 is always filled
level: 0, level: 0,
@@ -11,6 +11,6 @@ export default function aggregateClassLevel({node, linkedNode, link}){
aggregator.levelsFilled[linkedProp.level] = true; aggregator.levelsFilled[linkedProp.level] = true;
} else if (link.data === 'level'){ } else if (link.data === 'level'){
node.data.baseValue = (node.data.baseValue || 0) + node.data.baseValue = (node.data.baseValue || 0) +
linkedNode.data.classLevelAggregator.level; (linkedNode.data.classLevelAggregator?.level || 0);
} }
} }

View File

@@ -1,6 +1,7 @@
import computeToggles from '/imports/api/engine/computation/computeComputation/computeToggles.js'; import computeToggles from '/imports/api/engine/computation/computeComputation/computeToggles.js';
import computeByType from '/imports/api/engine/computation/computeComputation/computeByType.js'; import computeByType from '/imports/api/engine/computation/computeComputation/computeByType.js';
import embedInlineCalculations from './utility/embedInlineCalculations.js'; import embedInlineCalculations from './utility/embedInlineCalculations.js';
import path from 'ngraph.path';
export default function computeCreatureComputation(computation){ export default function computeCreatureComputation(computation){
const stack = []; const stack = [];
@@ -27,7 +28,7 @@ export default function computeCreatureComputation(computation){
} else { } else {
top._visitedChildren = true; top._visitedChildren = true;
// Push dependencies to graph to be computed first // Push dependencies to graph to be computed first
pushDependenciesToStack(top.id, graph, stack); pushDependenciesToStack(top.id, graph, stack, computation);
} }
} }
@@ -42,8 +43,20 @@ function compute(computation, node){
computeByType[node.data?.type || '_variable']?.(computation, node); computeByType[node.data?.type || '_variable']?.(computation, node);
} }
function pushDependenciesToStack(nodeId, graph, stack){ function pushDependenciesToStack(nodeId, graph, stack, computation){
graph.forEachLinkedNode(nodeId, linkedNode => { graph.forEachLinkedNode(nodeId, linkedNode => {
if (linkedNode._visitedChildren && !linkedNode._visited){
const pather = path.nba(graph, {
oriented: true
});
const loop = pather.find(nodeId, nodeId);
computation.errors.push({
type: 'dependencyLoop',
details: {
nodes: loop.map(node => node.id)
},
});
}
stack.push(linkedNode); stack.push(linkedNode);
}, true); }, true);
} }

View File

@@ -0,0 +1,9 @@
import Creatures from '/imports/api/creature/creatures/Creatures.js';
export default function(creatureId, errors = []){
if (errors.length){
Creatures.update(creatureId, {$set: {computeErrors: errors}});
} else {
Creatures.update(creatureId, {$unset: {computeErrors: 1}});
}
}

View File

@@ -2,12 +2,14 @@ import buildCreatureComputation from './computation/buildCreatureComputation.js'
import computeCreatureComputation from './computation/computeCreatureComputation.js'; import computeCreatureComputation from './computation/computeCreatureComputation.js';
import writeAlteredProperties from './computation/writeComputation/writeAlteredProperties.js'; import writeAlteredProperties from './computation/writeComputation/writeAlteredProperties.js';
import writeScope from './computation/writeComputation/writeScope.js'; import writeScope from './computation/writeComputation/writeScope.js';
import writeErrors from './computation/writeComputation/writeErrors.js';
export default function computeCreature(creatureId){ export default function computeCreature(creatureId){
const computation = buildCreatureComputation(creatureId); const computation = buildCreatureComputation(creatureId);
computeCreatureComputation(computation); computeCreatureComputation(computation);
writeAlteredProperties(computation); writeAlteredProperties(computation);
writeScope(creatureId, computation.scope); writeScope(creatureId, computation.scope);
writeErrors(creatureId, computation.errors);
} }
// For now just recompute the whole creature, TODO only recompute a single // For now just recompute the whole creature, TODO only recompute a single

View File

@@ -4,7 +4,7 @@
v-on="hasClickListener ? {click} : {}" v-on="hasClickListener ? {click} : {}"
> >
<v-list-item-content> <v-list-item-content>
<v-list-item-title> <v-list-item-title v-if="Number.isFinite(model.total)">
<div <div
v-if="model.total > 4" v-if="model.total > 4"
class="layout value" class="layout value"
@@ -36,6 +36,11 @@
</v-icon> </v-icon>
</div> </div>
</v-list-item-title> </v-list-item-title>
<v-list-item-title v-else>
<code>
{{model.total}}
</code>
</v-list-item-title>
<v-list-item-subtitle> <v-list-item-subtitle>
{{ model.name }} {{ model.name }}
</v-list-item-subtitle> </v-list-item-subtitle>

9
app/package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "dicecloud", "name": "dicecloud",
"version": "0.10.0", "version": "2.0.33",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -2399,6 +2399,11 @@
"ngraph.events": "^1.2.1" "ngraph.events": "^1.2.1"
} }
}, },
"ngraph.path": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/ngraph.path/-/ngraph.path-1.4.0.tgz",
"integrity": "sha512-yJZay4tP0wcjqkkf8zlMQ/T+JOgU+EWfdE4w4TG8OS94B12J/+Z44UOYxVJErE8E6/wFunX1hMZEB1/GHsBYHg=="
},
"node-addon-api": { "node-addon-api": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz",
@@ -2775,7 +2780,7 @@
}, },
"signal-exit": { "signal-exit": {
"version": "3.0.2", "version": "3.0.2",
"resolved": false, "resolved": "",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
}, },
"simpl-schema": { "simpl-schema": {

View File

@@ -38,6 +38,7 @@
"moo": "^0.5.1", "moo": "^0.5.1",
"nearley": "^2.19.1", "nearley": "^2.19.1",
"ngraph.graph": "^19.1.0", "ngraph.graph": "^19.1.0",
"ngraph.path": "^1.4.0",
"qrcode": "^1.5.0", "qrcode": "^1.5.0",
"request": "^2.88.2", "request": "^2.88.2",
"simpl-schema": "^1.12.0", "simpl-schema": "^1.12.0",