Implementing persisting action result mutations
This commit is contained in:
@@ -3,16 +3,24 @@ import { getSingleProperty } from '/imports/api/engine/loadCreatures';
|
||||
import applyTask from '/imports/api/engine/action/tasks/applyTask'
|
||||
import InputProvider from '/imports/api/engine/action/functions/userInput/InputProvider';
|
||||
import saveInputChoices from './userInput/saveInputChoices';
|
||||
import Task from '/imports/api/engine/action/tasks/Task';
|
||||
|
||||
// TODO create a function to get the effective value of a property,
|
||||
// simulating all the result updates in the action so far
|
||||
|
||||
// Apply an action
|
||||
// This is run once as a simulation on the client awaiting all the various inputs or step through
|
||||
// clicks from the user, then it is run as part of the runAction method, where it is expected to
|
||||
// complete instantly on the client, and sent to the server as a method call
|
||||
/**
|
||||
* Apply an action
|
||||
* This is run once as a simulation on the client awaiting all the various inputs or step through
|
||||
* clicks from the user, then it is run as part of the runAction method, where it is expected to
|
||||
* complete instantly on the client, and sent to the server as a method call
|
||||
* @param action The action to apply
|
||||
* @param userInput The input provider
|
||||
* @param { Object } options
|
||||
* @param { Task } options.task If provided, the action will start with this task instead of
|
||||
* applying the root property of the action
|
||||
*/
|
||||
export default async function applyAction(action: EngineAction, userInput: InputProvider, options?: {
|
||||
simulate?: boolean, stepThrough?: boolean
|
||||
simulate?: boolean, stepThrough?: boolean, task?: Task,
|
||||
}) {
|
||||
const { simulate, stepThrough } = options || {};
|
||||
if (!simulate && stepThrough) throw 'Cannot step through unless simulating';
|
||||
@@ -31,12 +39,15 @@ export default async function applyAction(action: EngineAction, userInput: Input
|
||||
action._stepThrough = stepThrough;
|
||||
action._isSimulation = simulate;
|
||||
action.taskCount = 0;
|
||||
console.log(JSON.stringify(action, null, 2));
|
||||
const prop = await getSingleProperty(action.creatureId, action.rootPropId);
|
||||
if (!prop) throw new Meteor.Error('Not found', 'Root action property could not be found');
|
||||
await applyTask(action, {
|
||||
prop,
|
||||
targetIds: action.targetIds || [],
|
||||
}, userInput);
|
||||
return { action, userInput };
|
||||
let task = options?.task;
|
||||
if (!task) {
|
||||
const prop = await getSingleProperty(action.creatureId, action.rootPropId);
|
||||
if (!prop) throw new Meteor.Error('Not found', 'Root action property could not be found');
|
||||
task = options?.task || {
|
||||
prop,
|
||||
targetIds: action.targetIds || [],
|
||||
}
|
||||
}
|
||||
await applyTask(action, task, userInput);
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Mutation } from '/imports/api/engine/action/tasks/TaskResult';
|
||||
|
||||
export default function mutationToLogUpdates(mutation: Mutation) {
|
||||
if (!mutation.contents) return [];
|
||||
const contents: any[] = [];
|
||||
for (const content of mutation.contents) {
|
||||
contents.push({
|
||||
...content,
|
||||
targetIds: mutation.targetIds,
|
||||
});
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Mutation } from '/imports/api/engine/action/tasks/TaskResult';
|
||||
import { newOperation } from '/imports/api/engine/shared/bulkWrite';
|
||||
|
||||
export default function mutationToPropUpdates(mutation: Mutation) {
|
||||
const bulkWriteOps: any[] = [];
|
||||
// Updates to creature properties
|
||||
if (mutation.updates) {
|
||||
const propUpdatesById: Record<string, any> = {};
|
||||
for (const update of mutation.updates) {
|
||||
if (!propUpdatesById[update.propId]) {
|
||||
propUpdatesById[update.propId] = newOperation(update.propId);
|
||||
}
|
||||
if (update.set) {
|
||||
propUpdatesById[update.propId].updateOne.update.$set = {
|
||||
...propUpdatesById[update.propId].updateOne.update.$set,
|
||||
...update.set,
|
||||
};
|
||||
}
|
||||
if (update.inc) {
|
||||
propUpdatesById[update.propId].updateOne.update.$inc = {
|
||||
...propUpdatesById[update.propId].updateOne.update.$inc,
|
||||
...update.inc,
|
||||
};
|
||||
}
|
||||
}
|
||||
for (const id in propUpdatesById) {
|
||||
bulkWriteOps.push(propUpdatesById[id]);
|
||||
}
|
||||
}
|
||||
// Insert creature properties
|
||||
if (mutation.inserts) for (const insertOne of mutation.inserts) {
|
||||
bulkWriteOps.push({
|
||||
insertOne
|
||||
});
|
||||
}
|
||||
// Remove creature properties
|
||||
if (mutation.removals) for (const removeOne of mutation.removals) {
|
||||
bulkWriteOps.push({
|
||||
removeOne,
|
||||
});
|
||||
}
|
||||
return bulkWriteOps;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import EngineActions, { EngineAction } from '/imports/api/engine/action/EngineActions';
|
||||
import mutationToPropUpdates from './mutationToPropUpdates';
|
||||
import mutationToLogUpdates from '/imports/api/engine/action/functions/mutationToLogUpdates';
|
||||
import { union } from 'lodash';
|
||||
import CreatureLogs from '/imports/api/creature/log/CreatureLogs';
|
||||
import bulkWrite from '/imports/api/engine/shared/bulkWrite';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
|
||||
export default async function writeActionResults(action: EngineAction) {
|
||||
if (!action._id) throw new Meteor.Error('type-error', 'Action does not have an _id');
|
||||
EngineActions.remove(action._id);
|
||||
const creaturePropUpdates: any[] = [];
|
||||
const logContents: any[] = [];
|
||||
// Collect all the updates and log content
|
||||
action.results.forEach(result => {
|
||||
result.mutations.forEach(mutation => {
|
||||
creaturePropUpdates.push(...mutationToPropUpdates(mutation));
|
||||
logContents.push(...mutationToLogUpdates(mutation));
|
||||
});
|
||||
});
|
||||
const allTargetIds = union(...logContents.map(c => c.targetIds));
|
||||
|
||||
// Write the log
|
||||
const logPromise = CreatureLogs.insertAsync({
|
||||
content: logContents,
|
||||
creatureId: action.creatureId,
|
||||
targetIds: allTargetIds,
|
||||
});
|
||||
|
||||
// Write the bulk updates
|
||||
const bulkWritePromise = bulkWrite(creaturePropUpdates, CreatureProperties);
|
||||
|
||||
return Promise.all([logPromise, bulkWritePromise]);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { isEmpty } from 'lodash'
|
||||
import EngineActions, { EngineAction, ActionSchema } from '/imports/api/engine/action/EngineActions';
|
||||
|
||||
export default async function writeChangedAction(original: EngineAction, changed: EngineAction) {
|
||||
const $set = {};
|
||||
for (const key of ActionSchema.objectKeys()) {
|
||||
if (!EJSON.equals(original[key], changed[key])) {
|
||||
$set[key] = changed[key];
|
||||
}
|
||||
}
|
||||
if (!isEmpty($set) && original._id) {
|
||||
return EngineActions.updateAsync(original._id, { $set });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user