State Patching
The @shell/serialization package supports two modes of state application: apply() for authoritative live patching and load() for full state replacement.
apply() vs load()
Section titled “apply() vs load()”load() | apply() | |
|---|---|---|
| Lifecycle | Stops, restarts | No interruption |
| Existing entities | Wiped | Patched (spawns missing, updates existing, despawns stale) |
| Use case | Scene loading, save files | Networking, live state sync |
apply() — authoritative state sync
Section titled “apply() — authoritative state sync”apply() treats incoming data as the desired world state and reconciles the live world:
serializer.apply(data);
// Scoped authority — only managed entities can be despawnedserializer.apply(data, serverOwnedEntities);Only serializable components are candidates for removal. Non-serializable components (tags, internal state managed by systems/plugins) are left untouched.
load() — full state replacement
Section titled “load() — full state replacement”await serializer.load(data);await serializer.load(data, { restartLifecycle: false });Wipes all entities, deserializes, commits, and optionally restarts the lifecycle.
Field serializers
Section titled “Field serializers”Instance and Object store types are skipped during serialization unless a custom FieldSerializer is provided on the component.
import { Define, StoreType } from '@shell/ecs';
const MyComponent = Define.component('MyComponent') .withSchema({ data: { type: StoreType.Instance() }, }) .serializable() .withFieldSerializer('data', { serialize: (value) => ({ key: value.key }), deserialize: (data) => new MyData(data.key), });