Sites

Menu

Snapshots

All code examples in this section assume you have defined a MyComponents function as described in Providing components, and set up the following preamble:

#include <improbable/standard_library.h>
#include <improbable/worker.h>
#include <example.h>

using namespace improbable;

The SDK provides two classes to manipulate snapshots stored in files:

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 two public methods:

/**
 * Creates a SnapshotInputStream to read the snapshot file from the given path.
 * Returns a Result object which contains an error message and an error code of
 * type StreamErrorCode if error occurred.
 */
static Result<SnapshotInputStream, StreamErrorCode> Create(const ComponentRegistry& registry, const std::string& path);

/**
 * Reads the next EntityId and Entity pair from the Snapshot. Returns a Result
 * object which contains either the read pair or an error. The error code can be
 * kBadState, kInvalidData or kEof.
 */
Result<std::pair<EntityId, Entity>, StreamErrorCode> ReadEntity();

SnapshotInputStream::HasNext is deprecated in favour of using StreamErrorCode::kEof to mark the end of a snapshot input stream.

SnapshotOutputStream

SnapshotOutputStream has two public methods:

/**
 * Creates a SnapshotOutputStream to write the snapshot file to the given path.
 * Returns a Result object which contains either a SnapshotOutputStream object,
 * or an error. The error code is kBadState.
 */
static Result<SnapshotOutputStream, StreamErrorCode> Create(const ComponentRegistry& registry, const std::string& path);

/**
 * Writes the EntityId and Entity pair to the output stream. Returns a Result
 * object which contains either None (void) or an error. The error code can be
 * kBadState or kInvalidData.
 */
Result<None, StreamErrorCode> 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. This example highlights the different errors which may arise after different snapshot calls, and you can choose how to handle them at your convenience.

bool AddLowHealthEffectToEntities(const std::string& snapshot_filename,
                                  const std::string& new_snapshot_filename) {
  // Create a SnapshotInputStream to read from the snapshot file.
  auto result_input_stream = worker::SnapshotInputStream::Create(MyComponents(), snapshot_filename);
  if (!result_input_stream) {
    // The snapshot failed to be initialized, and the stream is not usable
    // with error code kBadState.
    LogError(result_input_stream.GetErrorMessage());
    return false;
  }
  // Create a SnapshotOutputStream to write to a snapshot file.
  auto result_output_stream =
      worker::SnapshotOutputStream::Create(MyComponents(), new_snapshot_filename);
  if (!result_output_stream) {
    // The snapshot failed to be initialized, and the stream is not usable
    // with error code kBadState.
    LogError(result_input_stream.GetErrorMessage());
    return false;
  }

  // Add the "LowHealth" effect to all entities that have a Status component and
  // less than 10 health points.
  while (true) {
    auto result_snapshot_entity = result_input_stream->ReadEntity();
    if (!result_snapshot_entity) {
      auto read_error_code = result_snapshot_entity.GetErrorCode();
      if (read_error_code == worker::StreamErrorCode::kInvalidData) {
        // The last read operation failed, but the snapshot is still usable.
        // Log or handle the operation failure. Possible errors include
        // unregistered component or failed deserialization.
        LogError(result_snapshot_entity.GetErrorMessage());
        continue;
      }
      if (read_error_code == worker::StreamErrorCode::kBadState) {
        // An internal error occurred when reading and the snapshot is not usable.
        LogError(result_snapshot_entity.GetErrorMessage());
        return false;
      }
      if (read_error_code == worker::StreamErrorCode::kEof) {
        // The eof was reached when reading, replaces HasNext.
        return true;
      }

      auto creature = result_snapshot_entity->second.Get<example::Creature>();
      if (creature && creature->health() < 10) {
        creature->effects().emplace_back("LowHealth", 100);
      }

      // Save entity back to the snapshot file.
      auto result_write = result_output_stream->WriteEntity(result_snapshot_entity->first,
                                                            result_snapshot_entity->second);
      if (!result_write) {
        auto write_error_code = result_write.GetErrorCode();
        if (write_error_code == worker::StreamErrorCode::kInvalidData) {
          // The last 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 entities with the same id.
          LogError(result_write.GetErrorMessage());
          continue;
        }
        if (write_error_code == worker::StreamErrorCode::kBadState) {
          // An internal error occurred when writing and the snapshot is not usable.
          LogError(result_write.GetErrorMessage());
          return false;
        }
      }
    }
  }
}

Search results

Was this page helpful?

Thanks for letting us know!

Thanks for your feedback

Need more help? Ask on the forums