Implementing persisting action result mutations

This commit is contained in:
Thaum Rystra
2024-04-02 17:46:31 +02:00
parent 2cbfc5d099
commit 1fb1eb83c7
15 changed files with 285 additions and 144 deletions

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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]);
}

View File

@@ -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 });
}
}