Snapshots
The SDK provides two classes to manipulate snapshots stored in files:
worker::SnapshotInputStream
to read snapshot files from disk, one entity at a timeworker::SnapshotOutputStream
to write snapshot files to disk, one entity at a time.
This lets you write tools to perform offline transformations of the simulation state, or to programmatically create the starting point of a simulation.
Snapshot manipulation does not require a
Connection
, making it possible to
develop standalone, offline snapshot manipulation tools. However, we recommend using the build infrastructure
provided by SpatialOS for workers to build your standalone tools.
These stream classes are the recommended methods of manipulating snapshots as they do not require the entire snapshot to be stored in memory when reading or writing a snapshot.
SnapshotInputStream
SnapshotInputStream
has a constructor and two public methods:
/** Creates a SnapshotInputStream to read the snapshot file from the given path. */
explicit SnapshotInputStream(const ComponentRegistry& registry, const std::string& path);
/** Returns true if SnapshotInputStream has not reached EOF. */
bool HasNext();
/**
* Loads the next EntityId and Entity pair from the Snapshot. Returns an
* error message if error occurred.
*/
Option<std::string> ReadEntity(EntityId& entity_id, Entity& entity);
SnapshotOutputStream
SnapshotOutputStream
has a constructor and one public method:
/** Creates a SnapshotOutputStream to read the snapshot file from the given path. */
explicit SnapshotOutputStream(const ComponentRegistry& registry, const std::string& path);
/**
* Writes the EntityId and Entity pair to the output stream. Returns an
* error message if error occurred.
*/
Option<std::string> WriteEntity(EntityId entity_id, const Entity& entity);
When a SnapshotOutputStream
is destructed, the end of file is written and the stream’s resources are released.
Example
Here is an example of loading a snapshot, performing some manipulation on it, and saving it back. It uses the types and components defined in the example from Generated code.
worker::Option<std::string> AddLowHealthEffectToEntities(const std::string& snapshot_filename,
const std::string& new_snapshot_filename) {
// std::unordered_map<worker::EntityId, worker::Entity> entities;
// Create a SnapshotInputStream to read from the snapshot file.
worker::SnapshotInputStream input_stream{MyComponents(), snapshot_filename};
// Create a SnapshotOutputStream to write to a snapshot file.
worker::SnapshotOutputStream output_stream{MyComponents(), new_snapshot_filename};
// Add the "LowHealth" effect to all entities that have a Status component and less than 10 health
// points.
while (input_stream.HasNext()) {
worker::Entity entity;
worker::EntityId entity_id;
auto error_or_empty = input_stream.ReadEntity(entity_id, entity);
if (!error_or_empty.empty()) {
std::cerr << "Error reading entity from snapshot: " << *error_or_empty << std::endl;
return error_or_empty;
}
auto creature = entity.Get<example::Creature>();
if (creature && creature->health() < 10) {
creature->effects().emplace_back("LowHealth", 100);
}
// Save entity back to the snapshot file.
error_or_empty = output_stream.WriteEntity(entity_id, entity);
if (!error_or_empty.empty()) {
std::cerr << "Error saving entity to snapshot: " << *error_or_empty << std::endl;
return error_or_empty;
}
}
return {};
}