@antha/entity-2d 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/entity.d.ts CHANGED
@@ -182,6 +182,25 @@ export declare class EntityStore2d<State extends AnyObject = any> {
182
182
  * and then deserializing and passing the given `serializedParams` to that constructor.
183
183
  */
184
184
  deserializeEntity(entityKey: string, serializedParams: string | undefined): Promise<BaseEntity2d>;
185
+ /**
186
+ * Serializes every current entity, in update order, for {@link EntityStore2d.loadSnapshot}.
187
+ * Entities that are marked destroyed but not yet removed are skipped.
188
+ */
189
+ createSnapshot(): SerializedEntity2d[];
190
+ /**
191
+ * Hashes {@link EntityStore2d.createSnapshot} with `hashObject` from `@antha/util`, so it covers
192
+ * exactly what {@link BaseEntity2d.serialize} covers. Compare this across lock-step peers to
193
+ * detect desyncs.
194
+ */
195
+ hashEntities(): number;
196
+ /**
197
+ * Immediately destroys every current entity and recreates the entities from a snapshot made by
198
+ * {@link EntityStore2d.createSnapshot}, in the same order. Only entity params are restored: any
199
+ * state an entity keeps outside of its params is lost, so lock-step peers must all load the
200
+ * snapshot on the same frame to stay in sync. Every entity class in the snapshot must be
201
+ * registered in this store (see `preregisteredEntities`).
202
+ */
203
+ loadSnapshot(snapshot: ReadonlyArray<Readonly<SerializedEntity2d>>): Promise<BaseEntity2d<any, any, any>[]>;
185
204
  /** Create a new instance of the given entity class and add it to this entity store. */
186
205
  addEntity<const NewEntityConstructor extends Entity2dConstructor>(entityClass: NewEntityConstructor, ...params: AddEntity2dParams<NoInfer<NewEntityConstructor>>): Promise<InstanceType<NewEntityConstructor>>;
187
206
  /** Immediately destroys every current entity while leaving the entity store usable. */
@@ -189,6 +208,21 @@ export declare class EntityStore2d<State extends AnyObject = any> {
189
208
  /** Destroys the entity store and all entities contained inside it. */
190
209
  destroy(): void;
191
210
  }
211
+ /**
212
+ * Shape definition for {@link SerializedEntity2d}.
213
+ *
214
+ * @category Internal
215
+ */
216
+ export declare const serializedEntity2dShape: Shape<{
217
+ entityKey: string;
218
+ serializedParams: Shape<import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TUndefined, import("@sinclair/typebox").TString]>>>;
219
+ }>;
220
+ /**
221
+ * One entity in a snapshot from {@link EntityStore2d.createSnapshot}.
222
+ *
223
+ * @category Internal
224
+ */
225
+ export type SerializedEntity2d = typeof serializedEntity2dShape.runtimeType;
192
226
  /**
193
227
  * Shape definition for {@link EntityPositionParams}.
194
228
  *
package/dist/entity.js CHANGED
@@ -1,7 +1,8 @@
1
+ import { hashObject } from '@antha/util';
1
2
  import { assert, check } from '@augment-vir/assert';
2
- import { ConstructorInstanceMap, getObjectTypedEntries, getOrSet, makeWritable, mapObjectValues, } from '@augment-vir/common';
3
+ import { awaitedBlockingMap, ConstructorInstanceMap, getObjectTypedEntries, getOrSet, makeWritable, mapObjectValues, } from '@augment-vir/common';
3
4
  import { System as HitboxSystem, Response, } from 'detect-collisions';
4
- import { assertValidShape, defineShape } from 'object-shape-tester';
5
+ import { assertValidShape, defineShape, optionalShape } from 'object-shape-tester';
5
6
  import { ParticleContainer } from 'pixi.js';
6
7
  import { defineTypedCustomEvent, GenericListenTarget } from 'typed-event-target';
7
8
  export { System as HitboxSystem } from 'detect-collisions';
@@ -272,6 +273,48 @@ export class EntityStore2d {
272
273
  }
273
274
  return await this.addEntity(entityConstructor, entityConstructor.deserialize(serializedParams));
274
275
  }
276
+ /**
277
+ * Serializes every current entity, in update order, for {@link EntityStore2d.loadSnapshot}.
278
+ * Entities that are marked destroyed but not yet removed are skipped.
279
+ */
280
+ createSnapshot() {
281
+ if (this.isDestroyed) {
282
+ throw new Error('Cannot operate on a destroyed entity store.');
283
+ }
284
+ return [...this.currentEntityInstances]
285
+ .filter((entity) => !entity.isDestroyed)
286
+ .map((entity) => {
287
+ return {
288
+ entityKey: entity.entityDefinition.entityKey,
289
+ serializedParams: entity.serialize(),
290
+ };
291
+ });
292
+ }
293
+ /**
294
+ * Hashes {@link EntityStore2d.createSnapshot} with `hashObject` from `@antha/util`, so it covers
295
+ * exactly what {@link BaseEntity2d.serialize} covers. Compare this across lock-step peers to
296
+ * detect desyncs.
297
+ */
298
+ hashEntities() {
299
+ return hashObject(this.createSnapshot());
300
+ }
301
+ /**
302
+ * Immediately destroys every current entity and recreates the entities from a snapshot made by
303
+ * {@link EntityStore2d.createSnapshot}, in the same order. Only entity params are restored: any
304
+ * state an entity keeps outside of its params is lost, so lock-step peers must all load the
305
+ * snapshot on the same frame to stay in sync. Every entity class in the snapshot must be
306
+ * registered in this store (see `preregisteredEntities`).
307
+ */
308
+ async loadSnapshot(snapshot) {
309
+ this.destroyAllEntities();
310
+ /**
311
+ * `addEntity` inserts each entity only after its async init finishes, so creating them in
312
+ * parallel could order the store differently on each peer.
313
+ */
314
+ return await awaitedBlockingMap(snapshot, async (serializedEntity) => {
315
+ return await this.deserializeEntity(serializedEntity.entityKey, serializedEntity.serializedParams);
316
+ });
317
+ }
275
318
  /** Create a new instance of the given entity class and add it to this entity store. */
276
319
  async addEntity(entityClass, ...params) {
277
320
  if (this.isDestroyed) {
@@ -314,6 +357,17 @@ export class EntityStore2d {
314
357
  delete this.listenTarget;
315
358
  }
316
359
  }
360
+ /**
361
+ * Shape definition for {@link SerializedEntity2d}.
362
+ *
363
+ * @category Internal
364
+ */
365
+ export const serializedEntity2dShape = defineShape({
366
+ entityKey: '',
367
+ serializedParams: optionalShape('', {
368
+ alsoUndefined: true,
369
+ }),
370
+ });
317
371
  /**
318
372
  * Shape definition for {@link EntityPositionParams}.
319
373
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antha/entity-2d",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "An Antha mod for handling 2D entities.",
5
5
  "keywords": [
6
6
  "vir",
@@ -39,11 +39,12 @@
39
39
  "@augment-vir/common": "^32.3.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@antha/asset": "^0.19.0",
43
- "@antha/audio": "^0.19.0",
44
- "@antha/engine": "^0.19.0",
45
- "@antha/graphics-2d": "^0.19.0",
46
- "@antha/web-test-runner-plugin-pixi": "^0.19.0",
42
+ "@antha/asset": "^0.21.0",
43
+ "@antha/audio": "^0.21.0",
44
+ "@antha/engine": "^0.21.0",
45
+ "@antha/graphics-2d": "^0.21.0",
46
+ "@antha/util": "^0.21.0",
47
+ "@antha/web-test-runner-plugin-pixi": "^0.21.0",
47
48
  "@augment-vir/test": "^32.3.0",
48
49
  "@web/dev-server-esbuild": "^2.0.0",
49
50
  "@web/test-runner": "^1.0.0",
@@ -55,10 +56,11 @@
55
56
  "typed-event-target": "^4.3.3"
56
57
  },
57
58
  "peerDependencies": {
58
- "@antha/asset": "^0.19.0",
59
- "@antha/audio": "^0.19.0",
60
- "@antha/engine": "^0.19.0",
61
- "@antha/graphics-2d": "^0.19.0",
59
+ "@antha/asset": "^0.21.0",
60
+ "@antha/audio": "^0.21.0",
61
+ "@antha/engine": "^0.21.0",
62
+ "@antha/graphics-2d": "^0.21.0",
63
+ "@antha/util": "^0.21.0",
62
64
  "detect-collisions": "^10",
63
65
  "element-vir": "^27",
64
66
  "object-shape-tester": "^6",