Statements
Statements are the fundamental building blocks for actions within the SDK. Whether you are renaming a node, deleting a point cloud, or exporting a report, that logic should be wrapped in a Statement.
By using Statements, you ensure that plugin actions are robust, serializable, and integrated with the versioning and scripting systems.
Serializability
Statements are serializable. Because actions can be represented as data, the SDK provides several features for professional applications:
- Macros: Sequences of actions can be recorded into a macro and replayed.
- Recovery: If the client crashes, the session journal of statements can be used to restore the project or debug the failure.
- Automated Testing: Integration tests can be written by providing a list of statements and asserting the final state, without launching the 3D viewport.
Mutations vs. Effects
There are two types of statements based on how they interact with project history:
1. Mutations (Undoable Changes)
A Mutation directly changes the versioned state of the project. The SDK handles undo/redo logic automatically. If a user deletes an object via a mutation and performs an undo, the SDK restores that object from the version history.
Whenever a mutation is applied, the SDK uses Bevy Autosync to ensure the engine’s ECS state reflects the authoritative project state.
#[typetag::serde]
impl Mutation for RenameNode {
fn apply<'a, 'b>(&self, fse: &'a mut FseFacade<'_>) -> BoxFuture<'b, anyhow::Result<()>>
where 'a: 'b
{
Box::pin(async move {
// Update the versioned node state
let mut node = fse.get_object(self.id)?;
node.name = self.new_name.clone();
fse.set_object(self.id, node)?;
Ok(())
})
}
}2. Effects (Side Effects)
An Effect interacts with the outside world or transient state without changing the versioned project. Effects can read the project state, but they do not create new versions in the database. Examples include exporting data to a CSV, showing a UI notification, or sending a telemetry event.
Transactions
Often, a user action is composed of multiple mutations. For example, transforming an object might involve selecting it and then adjusting its position several times. You may want these actions grouped into a single atomic undo step.
Transactions allow you to group multiple mutations together. If any part of the transaction fails, the entire set of changes is discarded, ensuring the project remains in a consistent state.
Execution and Scripting
Statements are typically triggered by Tools when a user interacts with the UI or viewport. Because they are data-driven, they can also be executed by Runtime Extensions. This means features built for the client are available to third-party developers automating workflows via WASM scripts.