Skip to main content

ecsly_serialization 0.1.0 copy "ecsly_serialization: ^0.1.0" to clipboard
ecsly_serialization: ^0.1.0 copied to clipboard

Serialization and deserialization plugin for ecsly worlds: JSON snapshots of resources, entities, and SoA component columns.

ecsly_serialization #

Serialization and deserialization plugin for ecsly worlds.

Captures and restores JSON snapshots of:

  • Entities carrying a PersistentId component (identity + SoA columns + object-tier components);
  • Resources implementing SnapshotableResource;
  • Whole worlds via a versioned snapshot envelope (format v3).

Vision and boundaries: NORTH_STAR.md.

Identity model #

Runtime Entity handles are world-local and never serialized — they are regenerated on restore, exactly like Bevy and networked ECS engines treat entity ids. Stable identity lives in a component:

world.spawnComponents([
  const PersistentId(42), // stable cross-session identity
  const PositionComponent(),
]);

Only entities carrying PersistentId are captured; particles, VFX, and other transient entities are runtime-only by default. Restore spawns fresh entities into the target world — the target can be completely empty.

Install #

dart pub add ecsly_serialization

Workspace consumers inside this repo can use a key-only entry (ecsly_serialization:).

Usage #

A complete runnable save/load story lives in example/main.dart (dart run example/main.dart).

Whole world save/load #

// Mark persistable entities with PersistentId at spawn time.
final entity = world.spawnComponents([
  const PersistentId(1),
  const PositionComponent(),
  const HealthComponent(),
]);

final snapshot = captureWorldSnapshot(world);
final encoded = encodeWorldSnapshot(snapshot); // JSON string → disk

// Later — any fresh world, even a completely empty one:
final decoded = decodeWorldSnapshot(encoded);
final mapping = restoreWorldSnapshot(freshWorld, decoded);
// mapping: persistentId → newly spawned Entity

Restore is idempotent: re-applying the same snapshot leaves entities whose PersistentId already exists in the target world untouched.

Object-tier components #

Object components live in an ObjectColumn, so restore needs a way to build an instance. Register a sample at registration time (the component-side mirror of the event-side sampleEvent pattern):

world.components.registerObjectComponent<InventoryComponent>(
  sample: const InventoryComponent.empty(),
);

Alternatively pass per-call factories:

restoreWorldSnapshot(
  target,
  snapshot,
  componentFactories: {
    'InventoryComponent': () => const InventoryComponent.empty(),
  },
);

SoA/tag components need no sample — their columns are zero-initialized at spawn and overwritten from snapshot data. Object values are serialized via codecs registered in ObjectComponentCodecRegistry.

Structural changes between versions #

Component identity travels as type names, so registration reorder, additions, and removals are handled automatically. Renames and value transforms need an explicit migration chain keyed on schemaVersion:

final restored = decodeAndMigrateWorldSnapshot(oldSaveJson, [
  const RenameComponentMigration(
    fromVersion: 1,
    oldName: 'Transform',
    newName: 'PositionComponent',
  ),
], targetVersion: 2);

Resources #

class GameConfig extends Resource with SnapshotableResource {
  GameConfig({this.speed = 100});
  final double speed;

  @override
  Map<String, Object?> toJson() => {'speed': speed};

  static GameConfig fromJson(Map<String, Object?> json) =>
      GameConfig(speed: (json['speed'] as num).toDouble());
}

restoreWorldSnapshot(target, snapshot, resourceFactories: {
  'GameConfig': GameConfig.fromJson,
});

Plugin #

world.addPlugin(const SerializationPlugin());

Constraints #

  • Capture saves only PersistentId carriers; duplicate persistent ids fail capture loudly.
  • Restore requires object-tier components to have a registered sample or an explicit componentFactories entry — missing ones throw with the type name.
  • Renames and value transforms need an explicit SnapshotMigration chain; nothing is inferred automatically.

See DX_FAQ.md for recipes and DESIGN_FAQ.md for design rationale.

0
likes
160
points
235
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Serialization and deserialization plugin for ecsly worlds: JSON snapshots of resources, entities, and SoA component columns.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#ecs #serialization

License

MIT (license)

Dependencies

ecsly

More

Packages that depend on ecsly_serialization