@codehz/ecs 0.10.0 → 0.10.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.
- package/dist/builder.d.mts +11 -2
- package/dist/world.mjs +19 -6
- package/dist/world.mjs.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/component/singleton.test.ts +50 -5
- package/src/world/operations.ts +15 -3
- package/src/world/singleton.ts +3 -2
- package/src/world/world.ts +17 -1
package/package.json
CHANGED
|
@@ -3,6 +3,8 @@ import { ComponentEntityStore } from "../../component/entity-store";
|
|
|
3
3
|
import { component, createEntityId, relation, type EntityId } from "../../entity";
|
|
4
4
|
import { World } from "../../world/world";
|
|
5
5
|
|
|
6
|
+
function expectType<T>(_value: T): void {}
|
|
7
|
+
|
|
6
8
|
describe("World - Singleton Component", () => {
|
|
7
9
|
type GlobalConfig = { debug: boolean; version: string };
|
|
8
10
|
type GameState = { score: number; level: number };
|
|
@@ -26,21 +28,64 @@ describe("World - Singleton Component", () => {
|
|
|
26
28
|
const world = new World();
|
|
27
29
|
const singleton = world.singleton(GlobalConfigId);
|
|
28
30
|
const Marker = component<void>();
|
|
31
|
+
const originalWarn = console.warn;
|
|
32
|
+
const warnings: string[] = [];
|
|
33
|
+
|
|
34
|
+
console.warn = (...args: unknown[]) => {
|
|
35
|
+
warnings.push(args.join(" "));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
world.set(GlobalConfigId, Marker);
|
|
40
|
+
} finally {
|
|
41
|
+
console.warn = originalWarn;
|
|
42
|
+
}
|
|
29
43
|
|
|
30
|
-
world.set(GlobalConfigId, Marker);
|
|
31
44
|
world.sync();
|
|
32
45
|
|
|
33
46
|
expect(world.has(GlobalConfigId, Marker)).toBe(true);
|
|
34
47
|
expect(singleton.has()).toBe(false);
|
|
48
|
+
expect(warnings).toHaveLength(0);
|
|
35
49
|
});
|
|
36
50
|
|
|
37
|
-
it("should
|
|
51
|
+
it("should support the deprecated singleton data shorthand for non-number values", () => {
|
|
38
52
|
const world = new World();
|
|
39
53
|
const config: GlobalConfig = { debug: true, version: "1.0.0" };
|
|
54
|
+
const originalWarn = console.warn;
|
|
55
|
+
const warnings: string[] = [];
|
|
40
56
|
|
|
41
|
-
|
|
42
|
-
world.set(GlobalConfigId
|
|
43
|
-
}
|
|
57
|
+
if (false) {
|
|
58
|
+
expectType<void>(world.set(GlobalConfigId, config));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.warn = (...args: unknown[]) => {
|
|
62
|
+
warnings.push(args.join(" "));
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
world.set(GlobalConfigId, config);
|
|
67
|
+
} finally {
|
|
68
|
+
console.warn = originalWarn;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
world.sync();
|
|
72
|
+
|
|
73
|
+
expect(world.get(GlobalConfigId)).toEqual(config);
|
|
74
|
+
expect(warnings).toHaveLength(1);
|
|
75
|
+
expect(warnings[0]).toContain("deprecated");
|
|
76
|
+
expect(warnings[0]).toContain("world.singleton(componentId).set(value)");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should not expose the deprecated shorthand for numeric singleton types at the type level", () => {
|
|
80
|
+
const world = new World();
|
|
81
|
+
const Score = component<number>();
|
|
82
|
+
|
|
83
|
+
if (false) {
|
|
84
|
+
// @ts-expect-error Numeric singleton shorthand is intentionally unsupported.
|
|
85
|
+
expectType<void>(world.set(Score, 123));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
expect(true).toBe(true);
|
|
44
89
|
});
|
|
45
90
|
|
|
46
91
|
it("should manage singleton data through an explicit handle", () => {
|
package/src/world/operations.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentId, EntityId } from "../entity";
|
|
2
|
-
import { getDetailedIdType } from "../entity";
|
|
2
|
+
import { getDetailedIdType, isComponentId } from "../entity";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Validation and overload-resolution helpers extracted from World.
|
|
@@ -51,14 +51,26 @@ export function resolveSetOperation(
|
|
|
51
51
|
entityId: EntityId | ComponentId,
|
|
52
52
|
componentTypeOrComponent?: EntityId | any,
|
|
53
53
|
maybeComponent?: any,
|
|
54
|
+
argCount = 3,
|
|
54
55
|
exists: (id: EntityId) => boolean = () => true, // default permissive for tests / internal
|
|
55
|
-
): { entityId: EntityId; componentType: EntityId; component: any } {
|
|
56
|
+
): { entityId: EntityId; componentType: EntityId; component: any; deprecatedSingletonShorthand: boolean } {
|
|
56
57
|
const targetEntityId = entityId as EntityId;
|
|
58
|
+
|
|
59
|
+
if (argCount === 2 && isComponentId(targetEntityId) && typeof componentTypeOrComponent !== "number") {
|
|
60
|
+
assertEntityExists(targetEntityId, "Component entity", exists);
|
|
61
|
+
return {
|
|
62
|
+
entityId: targetEntityId,
|
|
63
|
+
componentType: targetEntityId,
|
|
64
|
+
component: componentTypeOrComponent,
|
|
65
|
+
deprecatedSingletonShorthand: true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
57
69
|
const componentType = componentTypeOrComponent as EntityId;
|
|
58
70
|
assertEntityExists(targetEntityId, "Entity", exists);
|
|
59
71
|
assertSetComponentTypeValid(componentType);
|
|
60
72
|
|
|
61
|
-
return { entityId: targetEntityId, componentType, component: maybeComponent };
|
|
73
|
+
return { entityId: targetEntityId, componentType, component: maybeComponent, deprecatedSingletonShorthand: false };
|
|
62
74
|
}
|
|
63
75
|
|
|
64
76
|
/**
|
package/src/world/singleton.ts
CHANGED
|
@@ -11,8 +11,9 @@ export interface SingletonHandleOps<T> {
|
|
|
11
11
|
/**
|
|
12
12
|
* Explicit handle for a singleton component (component-as-entity).
|
|
13
13
|
*
|
|
14
|
-
* This
|
|
15
|
-
*
|
|
14
|
+
* This is the preferred API for singleton components.
|
|
15
|
+
* `world.set(componentId, value)` remains available only as a deprecated
|
|
16
|
+
* compatibility shorthand.
|
|
16
17
|
*
|
|
17
18
|
* @example
|
|
18
19
|
* const config = world.singleton(Config);
|
package/src/world/world.ts
CHANGED
|
@@ -52,6 +52,9 @@ import { SingletonHandle } from "./singleton";
|
|
|
52
52
|
* Manages entities and components
|
|
53
53
|
*/
|
|
54
54
|
export class World {
|
|
55
|
+
private static readonly DEPRECATED_SINGLETON_SET_SHORTHAND_WARNING =
|
|
56
|
+
"world.set(componentId, value) for singleton components is deprecated; use world.singleton(componentId).set(value) or world.set(componentId, componentId, value) instead.";
|
|
57
|
+
|
|
55
58
|
// Core data structures for entity and archetype management
|
|
56
59
|
private entityIdManager = new EntityIdManager();
|
|
57
60
|
private entityReferences: EntityReferencesMap = new Map();
|
|
@@ -277,6 +280,10 @@ export class World {
|
|
|
277
280
|
* @overload set(entityId: EntityId, componentType: EntityId<void>): void
|
|
278
281
|
* Marks a void component as present on the entity
|
|
279
282
|
*
|
|
283
|
+
* @overload set<T>(componentId: ComponentId<T>, component: Exclude<NoInfer<T>, number>): void
|
|
284
|
+
* @deprecated Use `world.singleton(componentId).set(value)` or `world.set(componentId, componentId, value)` instead.
|
|
285
|
+
* Compatibility shorthand for singleton component data when the second argument is not a number
|
|
286
|
+
*
|
|
280
287
|
* @overload set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void
|
|
281
288
|
* Adds or updates a component with data on the entity
|
|
282
289
|
*
|
|
@@ -287,16 +294,25 @@ export class World {
|
|
|
287
294
|
* world.set(entity, Position, { x: 10, y: 20 });
|
|
288
295
|
* world.set(entity, Marker); // void component
|
|
289
296
|
* world.singleton(GlobalConfig).set({ debug: true }); // singleton component
|
|
297
|
+
* world.set(GlobalConfig, { debug: true }); // deprecated singleton compatibility shorthand
|
|
290
298
|
* world.sync(); // Apply changes
|
|
291
299
|
*/
|
|
292
300
|
set(entityId: EntityId, componentType: EntityId<void>): void;
|
|
301
|
+
/** @deprecated Use `world.singleton(componentId).set(value)` or `world.set(componentId, componentId, value)` instead. */
|
|
302
|
+
set<T>(componentId: ComponentId<T>, component: Exclude<NoInfer<T>, number>): void;
|
|
293
303
|
set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void;
|
|
294
304
|
set(entityId: EntityId | ComponentId, componentTypeOrComponent?: EntityId | any, maybeComponent?: any): void {
|
|
295
305
|
const {
|
|
296
306
|
entityId: targetEntityId,
|
|
297
307
|
componentType,
|
|
298
308
|
component,
|
|
299
|
-
|
|
309
|
+
deprecatedSingletonShorthand,
|
|
310
|
+
} = resolveSetOperation(entityId, componentTypeOrComponent, maybeComponent, arguments.length, (id) =>
|
|
311
|
+
this.exists(id),
|
|
312
|
+
);
|
|
313
|
+
if (deprecatedSingletonShorthand) {
|
|
314
|
+
console.warn(World.DEPRECATED_SINGLETON_SET_SHORTHAND_WARNING);
|
|
315
|
+
}
|
|
300
316
|
this.commandBuffer.set(targetEntityId, componentType, component);
|
|
301
317
|
}
|
|
302
318
|
|