Manipulating snapshots
The SDK provides two classes in to manipulate snapshots stored in files:
Improbable.Worker.SnapshotInputStream
to read snapshot files from disk, one entity at a timeImprobable.Worker.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 three public methods:
// Creates a SnapshotInputStream to read the snapshot file from the given path.
// Throws a StreamBadStateException if the SnapshotInputStream failed to be initialized and is not
// in a usable state. Further calls to the stream are ignored and this exception is re-thrown.
public SnapshotInputStream(string path)
// Releases the resources of the SnapshotInputStream.
public void Dispose()
// Reads the next EntityId and Entity pair from the Snapshot.
// Throws a StreamBadStateException if a snapshot internal error occurred and the stream is not
// in a usable state. Throws a StreamInvalidDataException if the last entity read operation on
// the snapshot stream failed and the stream is still in a usable state. Throws a
// System.IO.EndOfStreamException if the end of Snapshot was reached while trying to execute the
// last entity read operation.
// Returns a pair containing the read entity id and entity.
public System.Collections.Generic.KeyValuePair<EntityId, Entity> ReadEntity()
// Returns true if the SnapshotInputStream has not reached EOF.
public bool HasNext();
SnapshotOutputStream
SnapshotOutputStream
has a constructor and two public methods:
// Creates a SnapshotOutputStream to write to the snapshot file in the given path.
// Throws a StreamBadStateException if the SnapshotOutputStream failed to be initialized and is not
// in a usable state. Further calls to the stream are ignored and this exception is re-thrown.
public SnapshotOutputStream(string path)
// Writes the end of snapshot header and releases the resources of the SnapshotOutputStream.
public void Dispose()
// Writes the EntityId and Entity pair to the output stream.
// Throws a StreamBadStateException if a snapshot internal error occurred and the stream is not in
// a usable state. Throws a StreamInvalidDataException if the last entity write operation on the
// snapshot stream failed and the stream is still in a usable.
public void WriteEntity(EntityId entityId, Entity entity)
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. This example highlights the different errors which may arise after different snapshot calls, and you can choose how to handle them at your convenience.
public static void AddLowHealthEffectToEntities(string snapshotFilename, string newSnapshotFilename)
{
try
{
// Open a SnapshotInputStream to read from the snapshot file saved at snapshotFilename.
SnapshotInputStream inputStream = new SnapshotInputStream(snapshotFilename);
// Open a SnapshotOutputStream to write to a snapshot file at newSnapshotFilename.
SnapshotOutputStream outputStream = new SnapshotOutputStream(newSnapshotFilename);
// Iterate over every entity in the snapshot file.
while (inputStream.HasNext()) // this check can be done alternatively by catching EndOfStreamException.
{
try
{
var readEntity = inputStream.ReadEntity();
if (readEntity.Value.Get(Creature.Metaclass).HasValue)
{
var status = readEntity.Value.Get(Creature.Metaclass).Value;
// Add the "LowHealth" effect to all entities that have a Creature component
// and less than 10 health points.
if (status.health < 10)
{
status.effects.Add(new StatusEffect("LowHealth", 100));
}
}
// Write the (entityId, entity) pair to the snapshot file.
outputStream.WriteEntity(readEntity.Key, readEntity.Value);
}
catch (StreamInvalidDataException ex)
{
// The last read or write operation failed, but the snapshot is still usable.
// Log or handle the operation failure. Possible errors include unregistered component,
// failed serialization, missing Persistence component and writing entities with the same id.
Log(ex.Message);
}
catch (StreamBadStateException ex)
{
// An internal error occurred when reading or writing and the snapshot is not usable.
Log(ex.Message);
break;
}
catch (System.IO.EndOfStreamException ex)
{
// The eof was reached when reading. Not an error if used as alternative to HasNext.
Log(ex.Message);
break;
}
}
// Dispose of the inputStream to release the stream's resources.
inputStream.Dispose();
// Dispose of the outputStream to write the end of the Snapshot and release the stream's
// resources.
outputStream.Dispose();
}
catch (StreamBadStateException ex)
{
// The snapshot failed to be initialized, and the stream is not usable.
Log(ex.Message);
}
}