@codehz/ecs 0.8.0 → 0.8.1

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.
@@ -1,8 +1,41 @@
1
1
  import type { EntityId } from "../entity";
2
2
  import { MultiMap } from "../utils/multi-map";
3
3
 
4
+ /**
5
+ * Reverse reference index: maps each target entity to the set of (source entity, component) pairs
6
+ * that currently hold a reference to it.
7
+ *
8
+ * Used internally to support efficient entity deletion, including:
9
+ * - Fast-path deletion for unreferenced entities
10
+ * - Cascading deletes for relations marked with `cascadeDelete`
11
+ * - Automatic cleanup of entity-valued components and entity-relations when their target is destroyed
12
+ *
13
+ * Structure:
14
+ * targetEntityId -> MultiMap<sourceEntityId, componentOrRelationId>
15
+ *
16
+ * - For plain entity-valued components (component value is an EntityId):
17
+ * componentOrRelationId === the component type (which is also the entity id being pointed to)
18
+ * - For entity-relations (`relation(Comp, target)`):
19
+ * componentOrRelationId is the encoded (negative) relation ID
20
+ *
21
+ * This index is maintained in sync with structural changes via `updateEntityReferences` in World.
22
+ *
23
+ * @internal
24
+ */
4
25
  export type EntityReferencesMap = Map<EntityId, MultiMap<EntityId, EntityId>>;
5
26
 
27
+ /**
28
+ * Record that `sourceEntityId` holds a reference to `targetEntityId` via the given component/relation.
29
+ *
30
+ * Called when an entity-valued component or an entity-relation is added to an entity.
31
+ *
32
+ * @param entityReferences - The shared reverse index map
33
+ * @param sourceEntityId - The entity that contains the reference
34
+ * @param componentType - The component type or encoded relation ID used for the reference
35
+ * @param targetEntityId - The entity being referenced
36
+ *
37
+ * @internal
38
+ */
6
39
  export function trackEntityReference(
7
40
  entityReferences: EntityReferencesMap,
8
41
  sourceEntityId: EntityId,
@@ -15,6 +48,19 @@ export function trackEntityReference(
15
48
  entityReferences.get(targetEntityId)!.add(sourceEntityId, componentType);
16
49
  }
17
50
 
51
+ /**
52
+ * Remove the record that `sourceEntityId` references `targetEntityId` via the given component/relation.
53
+ *
54
+ * Called when an entity-valued component or entity-relation is removed (or during deletion).
55
+ * Automatically prunes empty target entries from the map.
56
+ *
57
+ * @param entityReferences - The shared reverse index map
58
+ * @param sourceEntityId - The entity that no longer holds the reference
59
+ * @param componentType - The component type or encoded relation ID that was used
60
+ * @param targetEntityId - The previously referenced entity
61
+ *
62
+ * @internal
63
+ */
18
64
  export function untrackEntityReference(
19
65
  entityReferences: EntityReferencesMap,
20
66
  sourceEntityId: EntityId,
@@ -30,6 +76,19 @@ export function untrackEntityReference(
30
76
  }
31
77
  }
32
78
 
79
+ /**
80
+ * Iterate over all (sourceEntityId, componentOrRelationId) pairs that currently reference the given target.
81
+ *
82
+ * Returns an empty iterable when the target has no incoming references.
83
+ * The returned iterable yields `[source, componentType]` pairs suitable for cleanup decisions
84
+ * (e.g. whether to cascade-delete the source or just remove the specific component/relation).
85
+ *
86
+ * @param entityReferences - The shared reverse index map
87
+ * @param targetEntityId - The entity whose referrers we want to inspect
88
+ * @returns Iterable of [sourceEntityId, componentOrRelationId]
89
+ *
90
+ * @internal
91
+ */
33
92
  export function getEntityReferences(
34
93
  entityReferences: EntityReferencesMap,
35
94
  targetEntityId: EntityId,
@@ -444,7 +444,10 @@ export class World {
444
444
  if (archetype.componentTypeSet.has(componentType)) return true;
445
445
 
446
446
  if (isDontFragmentRelation(componentType)) {
447
- return this.dontFragmentStore.get(entityId)?.has(componentType) ?? false;
447
+ // Use getValue; presence check via getAllForEntity only if value can legitimately be undefined
448
+ const val = this.dontFragmentStore.getValue(entityId, componentType);
449
+ if (val !== undefined) return true;
450
+ return this.dontFragmentStore.getAllForEntity(entityId).some(([t]) => t === componentType);
448
451
  }
449
452
 
450
453
  return false;
@@ -493,7 +496,11 @@ export class World {
493
496
  if (componentType >= 0 || componentType % RELATION_SHIFT !== 0) {
494
497
  const inArchetype = archetype.componentTypeSet.has(componentType);
495
498
  const hasDontFragment = isDontFragmentRelation(componentType);
496
- const hasComponent = inArchetype || (hasDontFragment && this.dontFragmentStore.get(entityId)?.has(componentType));
499
+ const hasComponent =
500
+ inArchetype ||
501
+ (hasDontFragment &&
502
+ (this.dontFragmentStore.getValue(entityId, componentType) !== undefined ||
503
+ this.dontFragmentStore.getAllForEntity(entityId).some(([t]) => t === componentType)));
497
504
 
498
505
  if (!hasComponent) {
499
506
  throw new Error(