@codehz/ecs 0.6.5 → 0.6.7
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/builder.d.mts +306 -1
- package/package.json +1 -1
- package/world.mjs +311 -3
- package/world.mjs.map +1 -1
package/builder.d.mts
CHANGED
|
@@ -405,6 +405,7 @@ type SerializedWorld = {
|
|
|
405
405
|
version: number;
|
|
406
406
|
entityManager: any;
|
|
407
407
|
entities: SerializedEntity[];
|
|
408
|
+
componentEntities?: SerializedEntity[];
|
|
408
409
|
};
|
|
409
410
|
type SerializedEntity = {
|
|
410
411
|
id: SerializedEntityId;
|
|
@@ -428,6 +429,8 @@ declare class World {
|
|
|
428
429
|
private archetypesByComponent;
|
|
429
430
|
private entityReferences;
|
|
430
431
|
private dontFragmentRelations;
|
|
432
|
+
private componentEntityComponents;
|
|
433
|
+
private relationEntityIdsByTarget;
|
|
431
434
|
private queries;
|
|
432
435
|
private queryCache;
|
|
433
436
|
private commandBuffer;
|
|
@@ -436,21 +439,188 @@ declare class World {
|
|
|
436
439
|
constructor(snapshot?: SerializedWorld);
|
|
437
440
|
private deserializeSnapshot;
|
|
438
441
|
private createArchetypeSignature;
|
|
442
|
+
/**
|
|
443
|
+
* Creates a new entity.
|
|
444
|
+
* The entity is created with an empty component set and can be configured using `set()`.
|
|
445
|
+
*
|
|
446
|
+
* @template T - The initial component type (defaults to void if not specified)
|
|
447
|
+
* @returns A unique identifier for the new entity
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* const entity = world.new<MyComponent>();
|
|
451
|
+
* world.set(entity, MyComponent, { value: 42 });
|
|
452
|
+
* world.sync();
|
|
453
|
+
*/
|
|
439
454
|
new<T = void>(): EntityId<T>;
|
|
455
|
+
private isComponentEntityId;
|
|
456
|
+
private registerRelationEntityId;
|
|
457
|
+
private unregisterRelationEntityId;
|
|
458
|
+
private getComponentEntityComponents;
|
|
459
|
+
private clearComponentEntityComponents;
|
|
460
|
+
private cleanupComponentEntitiesReferencingEntity;
|
|
440
461
|
private destroyEntityImmediate;
|
|
462
|
+
/**
|
|
463
|
+
* Checks if an entity exists in the world.
|
|
464
|
+
*
|
|
465
|
+
* @param entityId - The entity identifier to check
|
|
466
|
+
* @returns `true` if the entity exists, `false` otherwise
|
|
467
|
+
*
|
|
468
|
+
* @example
|
|
469
|
+
* if (world.exists(entityId)) {
|
|
470
|
+
* console.log("Entity exists");
|
|
471
|
+
* }
|
|
472
|
+
*/
|
|
441
473
|
exists(entityId: EntityId): boolean;
|
|
474
|
+
/**
|
|
475
|
+
* Adds or updates a component on an entity (or marks void component as present).
|
|
476
|
+
* The change is buffered and takes effect after calling `world.sync()`.
|
|
477
|
+
* If the entity does not exist, throws an error.
|
|
478
|
+
*
|
|
479
|
+
* @overload set(entityId: EntityId, componentType: EntityId<void>): void
|
|
480
|
+
* Marks a void component as present on the entity
|
|
481
|
+
*
|
|
482
|
+
* @overload set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void
|
|
483
|
+
* Adds or updates a component with data on the entity
|
|
484
|
+
*
|
|
485
|
+
* @throws {Error} If the entity does not exist
|
|
486
|
+
* @throws {Error} If the component type is invalid or is a wildcard relation
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* world.set(entity, Position, { x: 10, y: 20 });
|
|
490
|
+
* world.set(entity, Marker); // void component
|
|
491
|
+
* world.sync(); // Apply changes
|
|
492
|
+
*/
|
|
442
493
|
set(entityId: EntityId, componentType: EntityId<void>): void;
|
|
443
494
|
set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void;
|
|
495
|
+
/**
|
|
496
|
+
* Removes a component from an entity.
|
|
497
|
+
* The change is buffered and takes effect after calling `world.sync()`.
|
|
498
|
+
* If the entity does not exist, throws an error.
|
|
499
|
+
*
|
|
500
|
+
* @template T - The component data type
|
|
501
|
+
* @param entityId - The entity identifier
|
|
502
|
+
* @param componentType - The component type to remove
|
|
503
|
+
*
|
|
504
|
+
* @throws {Error} If the entity does not exist
|
|
505
|
+
* @throws {Error} If the component type is invalid
|
|
506
|
+
*
|
|
507
|
+
* @example
|
|
508
|
+
* world.remove(entity, Position);
|
|
509
|
+
* world.sync(); // Apply changes
|
|
510
|
+
*/
|
|
444
511
|
remove<T>(entityId: EntityId, componentType: EntityId<T>): void;
|
|
512
|
+
/**
|
|
513
|
+
* Deletes an entity and all its components from the world.
|
|
514
|
+
* The change is buffered and takes effect after calling `world.sync()`.
|
|
515
|
+
* Related entities may trigger cascade delete hooks if configured.
|
|
516
|
+
*
|
|
517
|
+
* @param entityId - The entity identifier to delete
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* world.delete(entity);
|
|
521
|
+
* world.sync(); // Apply changes
|
|
522
|
+
*/
|
|
445
523
|
delete(entityId: EntityId): void;
|
|
524
|
+
/**
|
|
525
|
+
* Checks if an entity has a specific component.
|
|
526
|
+
* Immediately reflects the current state without waiting for `sync()`.
|
|
527
|
+
*
|
|
528
|
+
* @template T - The component data type
|
|
529
|
+
* @param entityId - The entity identifier
|
|
530
|
+
* @param componentType - The component type to check
|
|
531
|
+
* @returns `true` if the entity has the component, `false` otherwise
|
|
532
|
+
*
|
|
533
|
+
* @example
|
|
534
|
+
* if (world.has(entity, Position)) {
|
|
535
|
+
* const pos = world.get(entity, Position);
|
|
536
|
+
* }
|
|
537
|
+
*/
|
|
446
538
|
has<T>(entityId: EntityId, componentType: EntityId<T>): boolean;
|
|
539
|
+
/**
|
|
540
|
+
* Retrieves a component from an entity.
|
|
541
|
+
* For wildcard relations, returns all relations of that type.
|
|
542
|
+
* Throws an error if the component does not exist; use `has()` to check first or use `getOptional()`.
|
|
543
|
+
*
|
|
544
|
+
* @overload get<T>(entityId: EntityId<T>): T
|
|
545
|
+
* When called with only an entity ID, retrieves the entity's primary component.
|
|
546
|
+
*
|
|
547
|
+
* @overload get<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, T][]
|
|
548
|
+
* For wildcard relations, returns an array of [target entity, component value] pairs.
|
|
549
|
+
*
|
|
550
|
+
* @overload get<T>(entityId: EntityId, componentType: EntityId<T>): T
|
|
551
|
+
* Retrieves a specific component from the entity.
|
|
552
|
+
*
|
|
553
|
+
* @throws {Error} If the entity does not exist
|
|
554
|
+
* @throws {Error} If the component does not exist on the entity
|
|
555
|
+
*
|
|
556
|
+
* @example
|
|
557
|
+
* const position = world.get(entity, Position); // Throws if no Position
|
|
558
|
+
* const relations = world.get(entity, relation(Parent, "*")); // Wildcard relation
|
|
559
|
+
*/
|
|
560
|
+
get<T>(entityId: EntityId<T>): T;
|
|
447
561
|
get<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, T][];
|
|
448
562
|
get<T>(entityId: EntityId, componentType: EntityId<T>): T;
|
|
563
|
+
/**
|
|
564
|
+
* Safely retrieves a component from an entity without throwing an error.
|
|
565
|
+
* Returns `undefined` if the component does not exist.
|
|
566
|
+
* For wildcard relations, returns `undefined` if there are no relations.
|
|
567
|
+
*
|
|
568
|
+
* @template T - The component data type
|
|
569
|
+
* @overload getOptional<T>(entityId: EntityId<T>): { value: T } | undefined
|
|
570
|
+
* Retrieves the entity's primary component safely.
|
|
571
|
+
*
|
|
572
|
+
* @overload getOptional<T>(entityId: EntityId, componentType: EntityId<T>): { value: T } | undefined
|
|
573
|
+
* Retrieves a specific component safely.
|
|
574
|
+
*
|
|
575
|
+
* @throws {Error} If the entity does not exist
|
|
576
|
+
*
|
|
577
|
+
* @example
|
|
578
|
+
* const position = world.getOptional(entity, Position);
|
|
579
|
+
* if (position) {
|
|
580
|
+
* console.log(position.value.x);
|
|
581
|
+
* }
|
|
582
|
+
*/
|
|
583
|
+
getOptional<T>(entityId: EntityId<T>): {
|
|
584
|
+
value: T;
|
|
585
|
+
} | undefined;
|
|
449
586
|
getOptional<T>(entityId: EntityId, componentType: EntityId<T>): {
|
|
450
587
|
value: T;
|
|
451
588
|
} | undefined;
|
|
452
589
|
/**
|
|
453
|
-
*
|
|
590
|
+
* Registers a lifecycle hook that responds to component changes.
|
|
591
|
+
* The hook callback is invoked when components matching the specified types are added, updated, or removed.
|
|
592
|
+
*
|
|
593
|
+
* @deprecated For single components, use the array overload with LifecycleCallback for better multi-component support
|
|
594
|
+
*
|
|
595
|
+
* @overload hook<T>(componentType: EntityId<T>, hook: LegacyLifecycleHook<T> | LegacyLifecycleCallback<T>): () => void
|
|
596
|
+
* Registers a hook for a single component type (legacy API).
|
|
597
|
+
*
|
|
598
|
+
* @overload hook<const T extends readonly ComponentType<any>[]>(
|
|
599
|
+
* componentTypes: T,
|
|
600
|
+
* hook: LifecycleHook<T> | LifecycleCallback<T>,
|
|
601
|
+
* ): () => void
|
|
602
|
+
* Registers a hook for multiple component types.
|
|
603
|
+
* The hook is triggered when all required components change together.
|
|
604
|
+
*
|
|
605
|
+
* @param componentTypesOrSingle - A single component type or an array of component types
|
|
606
|
+
* @param hook - Either a hook object with on_init/on_set/on_remove handlers, or a callback function
|
|
607
|
+
* @returns A function that unsubscribes the hook when called
|
|
608
|
+
*
|
|
609
|
+
* @throws {Error} If no required components are specified in array overload
|
|
610
|
+
*
|
|
611
|
+
* @example
|
|
612
|
+
* // Array overload (recommended)
|
|
613
|
+
* const unsubscribe = world.hook([Position, Velocity], {
|
|
614
|
+
* on_init: (entityId, position, velocity) => console.log("Initialized"),
|
|
615
|
+
* on_set: (entityId, position, velocity) => console.log("Updated"),
|
|
616
|
+
* on_remove: (entityId, position, velocity) => console.log("Removed"),
|
|
617
|
+
* });
|
|
618
|
+
* unsubscribe(); // Remove hook
|
|
619
|
+
*
|
|
620
|
+
* // Callback style
|
|
621
|
+
* const unsubscribe = world.hook([Position], (event, entityId, position) => {
|
|
622
|
+
* if (event === "init") console.log("Initialized");
|
|
623
|
+
* });
|
|
454
624
|
*/
|
|
455
625
|
hook<T>(componentType: EntityId<T>, hook: LegacyLifecycleHook<T> | LegacyLifecycleCallback<T>): () => void;
|
|
456
626
|
hook<const T extends readonly ComponentType<any>[]>(componentTypes: T, hook: LifecycleHook<T> | LifecycleCallback<T>): () => void;
|
|
@@ -458,21 +628,135 @@ declare class World {
|
|
|
458
628
|
unhook<T>(componentType: EntityId<T>, hook: LegacyLifecycleHook<T>): void;
|
|
459
629
|
/** @deprecated use the unsubscribe function returned by hook() instead */
|
|
460
630
|
unhook<const T extends readonly ComponentType<any>[]>(componentTypes: T, hook: LifecycleHook<T>): void;
|
|
631
|
+
/**
|
|
632
|
+
* Synchronizes all buffered commands (set/remove/delete) to the world.
|
|
633
|
+
* This method must be called after making changes via `set()`, `remove()`, or `delete()` for them to take effect.
|
|
634
|
+
* Typically called once per frame at the end of your game loop.
|
|
635
|
+
*
|
|
636
|
+
* @example
|
|
637
|
+
* world.set(entity, Position, { x: 10, y: 20 });
|
|
638
|
+
* world.remove(entity, OldComponent);
|
|
639
|
+
* world.sync(); // Apply all buffered changes
|
|
640
|
+
*/
|
|
461
641
|
sync(): void;
|
|
642
|
+
/**
|
|
643
|
+
* Creates a cached query for efficiently iterating entities with specific components.
|
|
644
|
+
* The query is cached internally and reused across calls with the same component types and filter.
|
|
645
|
+
*
|
|
646
|
+
* **Important:** Store the query reference and reuse it across frames for optimal performance.
|
|
647
|
+
* Creating a new query each frame defeats the caching mechanism.
|
|
648
|
+
*
|
|
649
|
+
* @param componentTypes - Array of component types to match
|
|
650
|
+
* @param filter - Optional filter for additional constraints (e.g., without specific components)
|
|
651
|
+
* @returns A Query instance that can be used to iterate matching entities
|
|
652
|
+
*
|
|
653
|
+
* @example
|
|
654
|
+
* // Create once, reuse many times
|
|
655
|
+
* const movementQuery = world.createQuery([Position, Velocity]);
|
|
656
|
+
*
|
|
657
|
+
* // In game loop
|
|
658
|
+
* movementQuery.forEach((entity) => {
|
|
659
|
+
* const pos = world.get(entity, Position);
|
|
660
|
+
* const vel = world.get(entity, Velocity);
|
|
661
|
+
* pos.x += vel.x;
|
|
662
|
+
* pos.y += vel.y;
|
|
663
|
+
* });
|
|
664
|
+
*
|
|
665
|
+
* // With filter
|
|
666
|
+
* const activeQuery = world.createQuery([Position], {
|
|
667
|
+
* without: [Disabled]
|
|
668
|
+
* });
|
|
669
|
+
*/
|
|
462
670
|
createQuery(componentTypes: EntityId<any>[], filter?: QueryFilter): Query;
|
|
671
|
+
/**
|
|
672
|
+
* Creates a new entity builder for fluent entity configuration.
|
|
673
|
+
* Useful for building entities with multiple components in a single expression.
|
|
674
|
+
*
|
|
675
|
+
* @returns An EntityBuilder instance
|
|
676
|
+
*
|
|
677
|
+
* @example
|
|
678
|
+
* const entity = world.spawn()
|
|
679
|
+
* .with(Position, { x: 0, y: 0 })
|
|
680
|
+
* .with(Velocity, { x: 1, y: 1 })
|
|
681
|
+
* .build();
|
|
682
|
+
* world.sync(); // Apply changes
|
|
683
|
+
*/
|
|
463
684
|
spawn(): EntityBuilder;
|
|
685
|
+
/**
|
|
686
|
+
* Spawns multiple entities with a configuration callback.
|
|
687
|
+
* More efficient than calling `spawn()` multiple times when creating many entities.
|
|
688
|
+
*
|
|
689
|
+
* @param count - Number of entities to spawn
|
|
690
|
+
* @param configure - Callback that receives an EntityBuilder and index; must return the configured builder
|
|
691
|
+
* @returns Array of created entity IDs
|
|
692
|
+
*
|
|
693
|
+
* @example
|
|
694
|
+
* const entities = world.spawnMany(100, (builder, index) => {
|
|
695
|
+
* return builder
|
|
696
|
+
* .with(Position, { x: index * 10, y: 0 })
|
|
697
|
+
* .with(Velocity, { x: 0, y: 1 });
|
|
698
|
+
* });
|
|
699
|
+
* world.sync();
|
|
700
|
+
*/
|
|
464
701
|
spawnMany(count: number, configure: (builder: EntityBuilder, index: number) => EntityBuilder): EntityId[];
|
|
465
702
|
_registerQuery(query: Query): void;
|
|
466
703
|
_unregisterQuery(query: Query): void;
|
|
704
|
+
/**
|
|
705
|
+
* Releases a cached query and frees its resources if no longer needed.
|
|
706
|
+
* Call this when you're done using a query to allow the world to clean up its cache entry.
|
|
707
|
+
*
|
|
708
|
+
* @param query - The query to release
|
|
709
|
+
*
|
|
710
|
+
* @example
|
|
711
|
+
* const query = world.createQuery([Position]);
|
|
712
|
+
* // ... use query ...
|
|
713
|
+
* world.releaseQuery(query); // Optional cleanup
|
|
714
|
+
*/
|
|
467
715
|
releaseQuery(query: Query): void;
|
|
716
|
+
/**
|
|
717
|
+
* Returns all archetypes that contain entities with the specified components.
|
|
718
|
+
* Used internally for query optimization but can be useful for debugging.
|
|
719
|
+
*
|
|
720
|
+
* @param componentTypes - Array of component types to match
|
|
721
|
+
* @returns Array of Archetype objects containing matching components
|
|
722
|
+
* @internal
|
|
723
|
+
*/
|
|
468
724
|
getMatchingArchetypes(componentTypes: EntityId<any>[]): Archetype[];
|
|
469
725
|
private getArchetypesWithComponents;
|
|
726
|
+
/**
|
|
727
|
+
* Queries entities with specific components.
|
|
728
|
+
* For simpler use cases, prefer using `createQuery()` with `forEach()` which is cached and more efficient.
|
|
729
|
+
*
|
|
730
|
+
* @overload query(componentTypes: EntityId<any>[]): EntityId[]
|
|
731
|
+
* Returns an array of entity IDs that have all specified components.
|
|
732
|
+
*
|
|
733
|
+
* @overload query<const T extends readonly EntityId<any>[]>(
|
|
734
|
+
* componentTypes: T,
|
|
735
|
+
* includeComponents: true,
|
|
736
|
+
* ): Array<{ entity: EntityId; components: ComponentTuple<T> }>
|
|
737
|
+
* Returns entities along with their component data.
|
|
738
|
+
*
|
|
739
|
+
* @param componentTypes - Array of component types to query
|
|
740
|
+
* @param includeComponents - If true, includes component data in results
|
|
741
|
+
* @returns Array of entity IDs or objects with entities and components
|
|
742
|
+
*
|
|
743
|
+
* @example
|
|
744
|
+
* // Just entity IDs
|
|
745
|
+
* const entities = world.query([Position, Velocity]);
|
|
746
|
+
*
|
|
747
|
+
* // With components
|
|
748
|
+
* const results = world.query([Position, Velocity], true);
|
|
749
|
+
* results.forEach(({ entity, components: [pos, vel] }) => {
|
|
750
|
+
* pos.x += vel.x;
|
|
751
|
+
* });
|
|
752
|
+
*/
|
|
470
753
|
query(componentTypes: EntityId<any>[]): EntityId[];
|
|
471
754
|
query<const T extends readonly EntityId<any>[]>(componentTypes: T, includeComponents: true): Array<{
|
|
472
755
|
entity: EntityId;
|
|
473
756
|
components: ComponentTuple<T>;
|
|
474
757
|
}>;
|
|
475
758
|
executeEntityCommands(entityId: EntityId, commands: Command[]): ComponentChangeset;
|
|
759
|
+
private executeComponentEntityCommands;
|
|
476
760
|
private createHooksContext;
|
|
477
761
|
private removeComponentImmediate;
|
|
478
762
|
private updateEntityReferences;
|
|
@@ -483,6 +767,27 @@ declare class World {
|
|
|
483
767
|
private archetypeReferencesEntity;
|
|
484
768
|
private cleanupArchetypesReferencingEntity;
|
|
485
769
|
private removeArchetype;
|
|
770
|
+
/**
|
|
771
|
+
* Serializes the entire world state to a plain JavaScript object.
|
|
772
|
+
* This creates a "memory snapshot" that can be stored or transmitted.
|
|
773
|
+
* The snapshot can be restored using `new World(snapshot)`.
|
|
774
|
+
*
|
|
775
|
+
* **Note:** This is NOT automatically persistent storage. To persist data,
|
|
776
|
+
* you must serialize the returned object to JSON or another format yourself.
|
|
777
|
+
*
|
|
778
|
+
* @returns A serializable object representing the world state
|
|
779
|
+
*
|
|
780
|
+
* @example
|
|
781
|
+
* // Create snapshot
|
|
782
|
+
* const snapshot = world.serialize();
|
|
783
|
+
*
|
|
784
|
+
* // Save to storage (example)
|
|
785
|
+
* localStorage.setItem('save', JSON.stringify(snapshot));
|
|
786
|
+
*
|
|
787
|
+
* // Later, restore from snapshot
|
|
788
|
+
* const savedData = JSON.parse(localStorage.getItem('save'));
|
|
789
|
+
* const newWorld = new World(savedData);
|
|
790
|
+
*/
|
|
486
791
|
serialize(): SerializedWorld;
|
|
487
792
|
}
|
|
488
793
|
//#endregion
|