@game_engine/physics-adapter 0.1.0-alpha.1 → 0.1.0-alpha.2

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/README.md CHANGED
@@ -1,14 +1,14 @@
1
- # @game_engine/physics-adapter
2
-
3
- Engine-neutral physics adapter contracts for Agent Engine.
4
-
5
- This package defines the boundary between Agent Engine ECS/source data and optional physics backends. It does not include a physics engine and must stay free of backend-specific dependencies.
6
-
7
- Adapter inputs are plain entities with `Transform`, `Velocity`, `Collider`, `RigidBody`, and `PhysicsMaterial` components. Adapter outputs are deterministic structured snapshots, contacts, grounding hints, and diagnostics.
8
-
9
- Use `comparePhysicsStepResults` when comparing optional backends. It summarizes body ids, static body positions, contact pairs, and diagnostic codes so tests can compare adapter health without requiring identical solver floating-point output.
10
-
11
- The current Rapier vs Planck evaluation is documented in
12
- [`docs/physics-adapter-evaluation.md`](../../docs/physics-adapter-evaluation.md).
13
-
14
- Default headless runtime behavior is still owned by `@game_engine/core` and its deterministic `KinematicPhysics` placeholder. Real physics adapters must be explicitly constructed by tests or experiments.
1
+ # @game_engine/physics-adapter
2
+
3
+ Engine-neutral physics adapter contracts for Agent Engine.
4
+
5
+ This package defines the boundary between Agent Engine ECS/source data and optional physics backends. It does not include a physics engine and must stay free of backend-specific dependencies.
6
+
7
+ Adapter inputs are plain entities with `Transform`, `Velocity`, `Collider`, `RigidBody`, and `PhysicsMaterial` components. Adapter outputs are deterministic structured snapshots, contacts, grounding hints, and diagnostics.
8
+
9
+ Use `comparePhysicsStepResults` when comparing optional backends. It summarizes body ids, static body positions, contact pairs, and diagnostic codes so tests can compare adapter health without requiring identical solver floating-point output.
10
+
11
+ The current Rapier vs Planck evaluation is documented in
12
+ [`docs/physics-adapter-evaluation.md`](../../docs/physics-adapter-evaluation.md).
13
+
14
+ Production headless and browser gameplay runtimes construct the Planck implementation of this boundary. `@game_engine/core` keeps deterministic `KinematicPhysics` only for explicit tests, references, and custom registries; Rapier remains an optional comparison backend.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { EntityId, ComponentName, ComponentValue, Vector2, Transform, Velocity, Collider, RigidBody, PhysicsMaterial, World } from '@game_engine/core';
1
+ import { EntityId, ComponentName, ComponentValue, Vector2, Transform, Velocity, PhysicsJoint, Collider, RigidBody, PhysicsMaterial, World } from '@game_engine/core';
2
2
 
3
3
  type PhysicsAdapterSeverity = "warning" | "error";
4
4
  type PhysicsBodyType = "static" | "dynamic" | "kinematic";
@@ -29,6 +29,13 @@ interface PhysicsContactSnapshot {
29
29
  otherEntityId: EntityId;
30
30
  grounded?: boolean;
31
31
  }
32
+ type PhysicsCollisionPhase = "start" | "stay" | "end";
33
+ interface PhysicsCollisionEvent {
34
+ phase: PhysicsCollisionPhase;
35
+ entityAId: EntityId;
36
+ entityBId: EntityId;
37
+ sensor: boolean;
38
+ }
32
39
  interface PhysicsGroundingSnapshot {
33
40
  grounded: boolean;
34
41
  groundEntityId?: EntityId;
@@ -41,17 +48,40 @@ interface PhysicsBodySnapshot {
41
48
  grounded?: PhysicsGroundingSnapshot;
42
49
  contacts: PhysicsContactSnapshot[];
43
50
  }
51
+ interface PhysicsJointSnapshot {
52
+ entityId: EntityId;
53
+ type: PhysicsJoint["type"];
54
+ bodyAId: EntityId;
55
+ bodyBId: EntityId;
56
+ }
57
+ interface PhysicsQueryFilter {
58
+ collisionLayers?: number[];
59
+ includeSensors?: boolean;
60
+ }
61
+ interface PhysicsQueryHit {
62
+ entityId: EntityId;
63
+ bodyType: PhysicsBodyType;
64
+ sensor: boolean;
65
+ point?: Vector2;
66
+ normal?: Vector2;
67
+ fraction?: number;
68
+ }
44
69
  interface PhysicsStepResult {
45
70
  ok: boolean;
46
71
  frame: number;
47
72
  deltaSeconds: number;
48
73
  bodies: PhysicsBodySnapshot[];
49
74
  contacts: PhysicsContactSnapshot[];
75
+ collisionEvents: PhysicsCollisionEvent[];
76
+ joints?: PhysicsJointSnapshot[];
50
77
  diagnostics: PhysicsAdapterDiagnostics;
51
78
  }
52
79
  interface PhysicsAdapterWorld {
53
80
  step(deltaSeconds: number, options?: PhysicsStepOptions): PhysicsStepResult;
54
81
  snapshot(options?: PhysicsStepOptions): PhysicsStepResult;
82
+ queryPoint?(point: Vector2, filter?: PhysicsQueryFilter): PhysicsQueryHit[];
83
+ queryAabb?(lowerBound: Vector2, upperBound: Vector2, filter?: PhysicsQueryFilter): PhysicsQueryHit[];
84
+ raycast?(from: Vector2, to: Vector2, filter?: PhysicsQueryFilter): PhysicsQueryHit[];
55
85
  dispose?(): void;
56
86
  }
57
87
  interface PhysicsAdapter {
@@ -64,6 +94,7 @@ interface PhysicsComponents {
64
94
  collider?: Collider;
65
95
  rigidBody?: RigidBody;
66
96
  physicsMaterial?: PhysicsMaterial;
97
+ physicsJoint?: PhysicsJoint;
67
98
  }
68
99
  interface PhysicsBackendComparisonInput {
69
100
  backend: string;
@@ -89,14 +120,19 @@ interface PhysicsBackendComparison {
89
120
  diagnosticsShapeCompatible: boolean;
90
121
  }
91
122
  declare function createPhysicsAdapterInputFromWorld(world: World): PhysicsAdapterWorldInput;
123
+ declare function applyPhysicsStepResultToWorld(world: World, result: PhysicsStepResult): void;
92
124
  declare function clonePhysicsAdapterInput(input: PhysicsAdapterWorldInput): PhysicsAdapterWorldInput;
93
125
  declare function readPhysicsComponents(entity: PhysicsAdapterEntity): PhysicsComponents;
94
126
  declare function resolvePhysicsBodyType(components: PhysicsComponents): PhysicsBodyType;
95
127
  declare function sortPhysicsBodySnapshots(bodies: PhysicsBodySnapshot[]): PhysicsBodySnapshot[];
96
128
  declare function sortPhysicsContacts(contacts: PhysicsContactSnapshot[]): PhysicsContactSnapshot[];
97
- declare function createPhysicsStepResult(fields: Omit<PhysicsStepResult, "ok" | "bodies" | "contacts" | "diagnostics"> & {
129
+ declare function sortPhysicsCollisionEvents(events: PhysicsCollisionEvent[]): PhysicsCollisionEvent[];
130
+ declare function sortPhysicsJointSnapshots(joints: PhysicsJointSnapshot[]): PhysicsJointSnapshot[];
131
+ declare function sortPhysicsQueryHits(hits: PhysicsQueryHit[]): PhysicsQueryHit[];
132
+ declare function createPhysicsStepResult(fields: Omit<PhysicsStepResult, "ok" | "bodies" | "contacts" | "collisionEvents" | "diagnostics"> & {
98
133
  bodies?: PhysicsBodySnapshot[];
99
134
  contacts?: PhysicsContactSnapshot[];
135
+ collisionEvents?: PhysicsCollisionEvent[];
100
136
  diagnostics?: PhysicsAdapterDiagnostics;
101
137
  }): PhysicsStepResult;
102
138
  declare function createPhysicsDiagnostic(diagnostic: PhysicsAdapterDiagnostic): PhysicsAdapterDiagnostic;
@@ -105,4 +141,4 @@ declare function comparePhysicsStepResults(inputs: PhysicsBackendComparisonInput
105
141
  declare function roundDeterministic(value: number): number;
106
142
  declare function roundVector2(value: Vector2): Vector2;
107
143
 
108
- export { type PhysicsAdapter, type PhysicsAdapterCreateOptions, type PhysicsAdapterDiagnostic, type PhysicsAdapterDiagnostics, type PhysicsAdapterEntity, type PhysicsAdapterSeverity, type PhysicsAdapterWorld, type PhysicsAdapterWorldInput, type PhysicsBackendComparison, type PhysicsBackendComparisonInput, type PhysicsBackendResultSummary, type PhysicsBodySnapshot, type PhysicsBodyType, type PhysicsComponents, type PhysicsContactSnapshot, type PhysicsGroundingSnapshot, type PhysicsStaticBodySummary, type PhysicsStepOptions, type PhysicsStepResult, clonePhysicsAdapterInput, comparePhysicsStepResults, createPhysicsAdapterInputFromWorld, createPhysicsDiagnostic, createPhysicsStepResult, readPhysicsComponents, resolvePhysicsBodyType, roundDeterministic, roundVector2, sortPhysicsBodySnapshots, sortPhysicsContacts, summarizePhysicsStepResult };
144
+ export { type PhysicsAdapter, type PhysicsAdapterCreateOptions, type PhysicsAdapterDiagnostic, type PhysicsAdapterDiagnostics, type PhysicsAdapterEntity, type PhysicsAdapterSeverity, type PhysicsAdapterWorld, type PhysicsAdapterWorldInput, type PhysicsBackendComparison, type PhysicsBackendComparisonInput, type PhysicsBackendResultSummary, type PhysicsBodySnapshot, type PhysicsBodyType, type PhysicsCollisionEvent, type PhysicsCollisionPhase, type PhysicsComponents, type PhysicsContactSnapshot, type PhysicsGroundingSnapshot, type PhysicsJointSnapshot, type PhysicsQueryFilter, type PhysicsQueryHit, type PhysicsStaticBodySummary, type PhysicsStepOptions, type PhysicsStepResult, applyPhysicsStepResultToWorld, clonePhysicsAdapterInput, comparePhysicsStepResults, createPhysicsAdapterInputFromWorld, createPhysicsDiagnostic, createPhysicsStepResult, readPhysicsComponents, resolvePhysicsBodyType, roundDeterministic, roundVector2, sortPhysicsBodySnapshots, sortPhysicsCollisionEvents, sortPhysicsContacts, sortPhysicsJointSnapshots, sortPhysicsQueryHits, summarizePhysicsStepResult };
package/dist/index.js CHANGED
@@ -4,6 +4,32 @@ function createPhysicsAdapterInputFromWorld(world) {
4
4
  entities: world.listEntities().map((entity) => cloneAdapterEntity(entity))
5
5
  };
6
6
  }
7
+ function applyPhysicsStepResultToWorld(world, result) {
8
+ for (const body of result.bodies) {
9
+ const entity = world.getEntity(body.entityId);
10
+ if (!entity) {
11
+ continue;
12
+ }
13
+ const transform = entity.components.Transform;
14
+ if (transform && body.transform) {
15
+ transform.position = [...body.transform.position];
16
+ transform.rotation = body.transform.rotation;
17
+ }
18
+ const velocity = entity.components.Velocity;
19
+ if (velocity && body.velocity) {
20
+ velocity.linear = [...body.velocity.linear];
21
+ }
22
+ const groundedState = entity.components.GroundedState;
23
+ if (groundedState) {
24
+ groundedState.grounded = body.grounded?.grounded === true;
25
+ if (body.grounded?.groundEntityId) {
26
+ groundedState.groundEntityId = body.grounded.groundEntityId;
27
+ } else {
28
+ delete groundedState.groundEntityId;
29
+ }
30
+ }
31
+ }
32
+ }
7
33
  function clonePhysicsAdapterInput(input) {
8
34
  return {
9
35
  entities: input.entities.map((entity) => cloneAdapterEntity(entity))
@@ -15,7 +41,8 @@ function readPhysicsComponents(entity) {
15
41
  velocity: readVelocity(entity.components.Velocity),
16
42
  collider: readCollider(entity.components.Collider),
17
43
  rigidBody: readRigidBody(entity.components.RigidBody),
18
- physicsMaterial: readPhysicsMaterial(entity.components.PhysicsMaterial)
44
+ physicsMaterial: readPhysicsMaterial(entity.components.PhysicsMaterial),
45
+ physicsJoint: readPhysicsJoint(entity.components.PhysicsJoint)
19
46
  };
20
47
  }
21
48
  function resolvePhysicsBodyType(components) {
@@ -33,9 +60,31 @@ function sortPhysicsContacts(contacts) {
33
60
  return entityOrder !== 0 ? entityOrder : left.otherEntityId.localeCompare(right.otherEntityId);
34
61
  });
35
62
  }
63
+ function sortPhysicsCollisionEvents(events) {
64
+ const phaseOrder = { start: 0, stay: 1, end: 2 };
65
+ return [...events].sort((left, right) => {
66
+ const entityAOrder = left.entityAId.localeCompare(right.entityAId);
67
+ if (entityAOrder !== 0) {
68
+ return entityAOrder;
69
+ }
70
+ const entityBOrder = left.entityBId.localeCompare(right.entityBId);
71
+ return entityBOrder !== 0 ? entityBOrder : phaseOrder[left.phase] - phaseOrder[right.phase];
72
+ });
73
+ }
74
+ function sortPhysicsJointSnapshots(joints) {
75
+ return [...joints].sort((left, right) => left.entityId.localeCompare(right.entityId));
76
+ }
77
+ function sortPhysicsQueryHits(hits) {
78
+ return [...hits].sort((left, right) => {
79
+ const fractionOrder = (left.fraction ?? 0) - (right.fraction ?? 0);
80
+ return fractionOrder !== 0 ? fractionOrder : left.entityId.localeCompare(right.entityId);
81
+ });
82
+ }
36
83
  function createPhysicsStepResult(fields) {
37
84
  const bodies = sortPhysicsBodySnapshots(fields.bodies ?? []);
38
85
  const contacts = sortPhysicsContacts(fields.contacts ?? []);
86
+ const collisionEvents = sortPhysicsCollisionEvents(fields.collisionEvents ?? []);
87
+ const joints = sortPhysicsJointSnapshots(fields.joints ?? []);
39
88
  const diagnostics = sortDiagnostics(fields.diagnostics ?? []);
40
89
  return {
41
90
  ok: !diagnostics.some((diagnostic) => diagnostic.severity === "error"),
@@ -43,6 +92,8 @@ function createPhysicsStepResult(fields) {
43
92
  deltaSeconds: roundDeterministic(fields.deltaSeconds),
44
93
  bodies,
45
94
  contacts,
95
+ collisionEvents,
96
+ ...fields.joints ? { joints } : {},
46
97
  diagnostics
47
98
  };
48
99
  }
@@ -138,7 +189,9 @@ function readCollider(value) {
138
189
  shape: "box",
139
190
  size,
140
191
  static: value.static,
141
- ...typeof value.sensor === "boolean" ? { sensor: value.sensor } : {}
192
+ ...typeof value.sensor === "boolean" ? { sensor: value.sensor } : {},
193
+ ...typeof value.collisionLayer === "number" ? { collisionLayer: value.collisionLayer } : {},
194
+ ...Array.isArray(value.collisionMask) && value.collisionMask.every((layer) => typeof layer === "number") ? { collisionMask: [...value.collisionMask] } : {}
142
195
  };
143
196
  }
144
197
  function readRigidBody(value) {
@@ -148,7 +201,8 @@ function readRigidBody(value) {
148
201
  return {
149
202
  type: value.type,
150
203
  ...typeof value.gravityScale === "number" ? { gravityScale: value.gravityScale } : {},
151
- ...typeof value.lockedRotation === "boolean" ? { lockedRotation: value.lockedRotation } : {}
204
+ ...typeof value.lockedRotation === "boolean" ? { lockedRotation: value.lockedRotation } : {},
205
+ ...typeof value.continuousCollisionDetection === "boolean" ? { continuousCollisionDetection: value.continuousCollisionDetection } : {}
152
206
  };
153
207
  }
154
208
  function readPhysicsMaterial(value) {
@@ -160,6 +214,49 @@ function readPhysicsMaterial(value) {
160
214
  restitution: value.restitution
161
215
  };
162
216
  }
217
+ function readPhysicsJoint(value) {
218
+ if (!isRecord(value) || typeof value.bodyA !== "string" || typeof value.bodyB !== "string") {
219
+ return void 0;
220
+ }
221
+ if (value.type === "distance") {
222
+ const anchorA = readVector2(value.anchorA);
223
+ const anchorB = readVector2(value.anchorB);
224
+ if (!anchorA || !anchorB) {
225
+ return void 0;
226
+ }
227
+ return {
228
+ type: "distance",
229
+ bodyA: value.bodyA,
230
+ bodyB: value.bodyB,
231
+ anchorA,
232
+ anchorB,
233
+ ...typeof value.length === "number" ? { length: value.length } : {},
234
+ ...typeof value.frequencyHz === "number" ? { frequencyHz: value.frequencyHz } : {},
235
+ ...typeof value.dampingRatio === "number" ? { dampingRatio: value.dampingRatio } : {},
236
+ ...typeof value.collideConnected === "boolean" ? { collideConnected: value.collideConnected } : {}
237
+ };
238
+ }
239
+ if (value.type === "revolute") {
240
+ const anchor = readVector2(value.anchor);
241
+ if (!anchor) {
242
+ return void 0;
243
+ }
244
+ return {
245
+ type: "revolute",
246
+ bodyA: value.bodyA,
247
+ bodyB: value.bodyB,
248
+ anchor,
249
+ ...typeof value.enableLimit === "boolean" ? { enableLimit: value.enableLimit } : {},
250
+ ...typeof value.lowerAngle === "number" ? { lowerAngle: value.lowerAngle } : {},
251
+ ...typeof value.upperAngle === "number" ? { upperAngle: value.upperAngle } : {},
252
+ ...typeof value.enableMotor === "boolean" ? { enableMotor: value.enableMotor } : {},
253
+ ...typeof value.motorSpeed === "number" ? { motorSpeed: value.motorSpeed } : {},
254
+ ...typeof value.maxMotorTorque === "number" ? { maxMotorTorque: value.maxMotorTorque } : {},
255
+ ...typeof value.collideConnected === "boolean" ? { collideConnected: value.collideConnected } : {}
256
+ };
257
+ }
258
+ return void 0;
259
+ }
163
260
  function readVector2(value) {
164
261
  if (!Array.isArray(value) || value.length !== 2 || typeof value[0] !== "number" || typeof value[1] !== "number" || !Number.isFinite(value[0]) || !Number.isFinite(value[1])) {
165
262
  return void 0;
@@ -188,6 +285,7 @@ function staticBodySummariesEqual(left, right) {
188
285
  });
189
286
  }
190
287
  export {
288
+ applyPhysicsStepResultToWorld,
191
289
  clonePhysicsAdapterInput,
192
290
  comparePhysicsStepResults,
193
291
  createPhysicsAdapterInputFromWorld,
@@ -198,7 +296,10 @@ export {
198
296
  roundDeterministic,
199
297
  roundVector2,
200
298
  sortPhysicsBodySnapshots,
299
+ sortPhysicsCollisionEvents,
201
300
  sortPhysicsContacts,
301
+ sortPhysicsJointSnapshots,
302
+ sortPhysicsQueryHits,
202
303
  summarizePhysicsStepResult
203
304
  };
204
305
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n Collider,\n ComponentName,\n ComponentValue,\n EntityId,\n EntityRecord,\n PhysicsMaterial,\n RigidBody,\n Transform,\n Vector2,\n Velocity,\n World\n} from \"@game_engine/core\";\n\nexport type PhysicsAdapterSeverity = \"warning\" | \"error\";\nexport type PhysicsBodyType = \"static\" | \"dynamic\" | \"kinematic\";\n\nexport interface PhysicsAdapterDiagnostic {\n severity: PhysicsAdapterSeverity;\n code: string;\n message: string;\n entityId?: EntityId;\n path?: string;\n suggestion?: string;\n}\n\nexport type PhysicsAdapterDiagnostics = PhysicsAdapterDiagnostic[];\n\nexport interface PhysicsAdapterEntity {\n id: EntityId;\n components: Record<ComponentName, ComponentValue>;\n}\n\nexport interface PhysicsAdapterWorldInput {\n entities: PhysicsAdapterEntity[];\n}\n\nexport interface PhysicsAdapterCreateOptions {\n gravity?: Vector2;\n}\n\nexport interface PhysicsStepOptions {\n frame?: number;\n}\n\nexport interface PhysicsContactSnapshot {\n entityId: EntityId;\n otherEntityId: EntityId;\n grounded?: boolean;\n}\n\nexport interface PhysicsGroundingSnapshot {\n grounded: boolean;\n groundEntityId?: EntityId;\n}\n\nexport interface PhysicsBodySnapshot {\n entityId: EntityId;\n bodyType: PhysicsBodyType;\n transform?: Pick<Transform, \"position\" | \"rotation\">;\n velocity?: Velocity;\n grounded?: PhysicsGroundingSnapshot;\n contacts: PhysicsContactSnapshot[];\n}\n\nexport interface PhysicsStepResult {\n ok: boolean;\n frame: number;\n deltaSeconds: number;\n bodies: PhysicsBodySnapshot[];\n contacts: PhysicsContactSnapshot[];\n diagnostics: PhysicsAdapterDiagnostics;\n}\n\nexport interface PhysicsAdapterWorld {\n step(deltaSeconds: number, options?: PhysicsStepOptions): PhysicsStepResult;\n snapshot(options?: PhysicsStepOptions): PhysicsStepResult;\n dispose?(): void;\n}\n\nexport interface PhysicsAdapter {\n name: string;\n createWorld(\n input: PhysicsAdapterWorldInput,\n options?: PhysicsAdapterCreateOptions\n ): PhysicsAdapterWorld | Promise<PhysicsAdapterWorld>;\n}\n\nexport interface PhysicsComponents {\n transform?: Transform;\n velocity?: Velocity;\n collider?: Collider;\n rigidBody?: RigidBody;\n physicsMaterial?: PhysicsMaterial;\n}\n\nexport interface PhysicsBackendComparisonInput {\n backend: string;\n result: PhysicsStepResult;\n}\n\nexport interface PhysicsStaticBodySummary {\n entityId: EntityId;\n position?: Vector2;\n}\n\nexport interface PhysicsBackendResultSummary {\n backend: string;\n ok: boolean;\n bodyCount: number;\n bodyIds: EntityId[];\n staticBodies: PhysicsStaticBodySummary[];\n diagnosticCodes: string[];\n contactPairs: string[];\n}\n\nexport interface PhysicsBackendComparison {\n summaries: PhysicsBackendResultSummary[];\n sameBodyIds: boolean;\n sameStaticBodyPositions: boolean;\n diagnosticsShapeCompatible: boolean;\n}\n\nexport function createPhysicsAdapterInputFromWorld(world: World): PhysicsAdapterWorldInput {\n return {\n entities: world.listEntities().map((entity) => cloneAdapterEntity(entity))\n };\n}\n\nexport function clonePhysicsAdapterInput(input: PhysicsAdapterWorldInput): PhysicsAdapterWorldInput {\n return {\n entities: input.entities.map((entity) => cloneAdapterEntity(entity))\n };\n}\n\nexport function readPhysicsComponents(entity: PhysicsAdapterEntity): PhysicsComponents {\n return {\n transform: readTransform(entity.components.Transform),\n velocity: readVelocity(entity.components.Velocity),\n collider: readCollider(entity.components.Collider),\n rigidBody: readRigidBody(entity.components.RigidBody),\n physicsMaterial: readPhysicsMaterial(entity.components.PhysicsMaterial)\n };\n}\n\nexport function resolvePhysicsBodyType(components: PhysicsComponents): PhysicsBodyType {\n if (components.rigidBody) {\n return components.rigidBody.type;\n }\n\n return components.collider?.static ? \"static\" : \"dynamic\";\n}\n\nexport function sortPhysicsBodySnapshots(bodies: PhysicsBodySnapshot[]): PhysicsBodySnapshot[] {\n return [...bodies].sort((left, right) => left.entityId.localeCompare(right.entityId));\n}\n\nexport function sortPhysicsContacts(contacts: PhysicsContactSnapshot[]): PhysicsContactSnapshot[] {\n return [...contacts].sort((left, right) => {\n const entityOrder = left.entityId.localeCompare(right.entityId);\n return entityOrder !== 0 ? entityOrder : left.otherEntityId.localeCompare(right.otherEntityId);\n });\n}\n\nexport function createPhysicsStepResult(\n fields: Omit<PhysicsStepResult, \"ok\" | \"bodies\" | \"contacts\" | \"diagnostics\"> & {\n bodies?: PhysicsBodySnapshot[];\n contacts?: PhysicsContactSnapshot[];\n diagnostics?: PhysicsAdapterDiagnostics;\n }\n): PhysicsStepResult {\n const bodies = sortPhysicsBodySnapshots(fields.bodies ?? []);\n const contacts = sortPhysicsContacts(fields.contacts ?? []);\n const diagnostics = sortDiagnostics(fields.diagnostics ?? []);\n\n return {\n ok: !diagnostics.some((diagnostic) => diagnostic.severity === \"error\"),\n frame: fields.frame,\n deltaSeconds: roundDeterministic(fields.deltaSeconds),\n bodies,\n contacts,\n diagnostics\n };\n}\n\nexport function createPhysicsDiagnostic(\n diagnostic: PhysicsAdapterDiagnostic\n): PhysicsAdapterDiagnostic {\n return { ...diagnostic };\n}\n\nexport function summarizePhysicsStepResult(\n backend: string,\n result: PhysicsStepResult\n): PhysicsBackendResultSummary {\n return {\n backend,\n ok: result.ok,\n bodyCount: result.bodies.length,\n bodyIds: result.bodies.map((body) => body.entityId).sort(),\n staticBodies: result.bodies\n .filter((body) => body.bodyType === \"static\")\n .map((body) => ({\n entityId: body.entityId,\n ...(body.transform?.position ? { position: roundVector2(body.transform.position) } : {})\n }))\n .sort((left, right) => left.entityId.localeCompare(right.entityId)),\n diagnosticCodes: result.diagnostics.map((diagnostic) => diagnostic.code).sort(),\n contactPairs: result.contacts\n .map((contact) => `${contact.entityId}->${contact.otherEntityId}`)\n .sort()\n };\n}\n\nexport function comparePhysicsStepResults(\n inputs: PhysicsBackendComparisonInput[]\n): PhysicsBackendComparison {\n const summaries = inputs\n .map((input) => summarizePhysicsStepResult(input.backend, input.result))\n .sort((left, right) => left.backend.localeCompare(right.backend));\n const [baseline] = summaries;\n\n return {\n summaries,\n sameBodyIds: baseline ? summaries.every((summary) => arraysEqual(summary.bodyIds, baseline.bodyIds)) : true,\n sameStaticBodyPositions: baseline\n ? summaries.every((summary) => staticBodySummariesEqual(summary.staticBodies, baseline.staticBodies))\n : true,\n diagnosticsShapeCompatible: baseline\n ? summaries.every((summary) => arraysEqual(summary.diagnosticCodes, baseline.diagnosticCodes))\n : true\n };\n}\n\nexport function roundDeterministic(value: number): number {\n const rounded = Number(value.toFixed(12));\n const nearestInteger = Math.round(rounded);\n return Math.abs(rounded - nearestInteger) < 1e-9 ? nearestInteger : rounded;\n}\n\nexport function roundVector2(value: Vector2): Vector2 {\n return [roundDeterministic(value[0]), roundDeterministic(value[1])];\n}\n\nfunction cloneAdapterEntity(entity: EntityRecord | PhysicsAdapterEntity): PhysicsAdapterEntity {\n return {\n id: entity.id,\n components: cloneComponents(entity.components)\n };\n}\n\nfunction cloneComponents(components: Record<ComponentName, ComponentValue>): Record<ComponentName, ComponentValue> {\n return Object.fromEntries(Object.entries(components).map(([name, value]) => [name, cloneValue(value)]));\n}\n\nfunction cloneValue(value: ComponentValue): ComponentValue {\n if (value === undefined) {\n return undefined;\n }\n\n return JSON.parse(JSON.stringify(value));\n}\n\nfunction sortDiagnostics(diagnostics: PhysicsAdapterDiagnostics): PhysicsAdapterDiagnostics {\n return [...diagnostics].sort((left, right) => {\n const entityOrder = (left.entityId ?? \"\").localeCompare(right.entityId ?? \"\");\n if (entityOrder !== 0) {\n return entityOrder;\n }\n\n const pathOrder = (left.path ?? \"\").localeCompare(right.path ?? \"\");\n return pathOrder !== 0 ? pathOrder : left.code.localeCompare(right.code);\n });\n}\n\nfunction readTransform(value: ComponentValue): Transform | undefined {\n if (!isRecord(value)) {\n return undefined;\n }\n\n const position = readVector2(value.position);\n const scale = readVector2(value.scale);\n const rotation = typeof value.rotation === \"number\" && Number.isFinite(value.rotation) ? value.rotation : undefined;\n\n return position && scale && rotation !== undefined\n ? {\n position,\n rotation,\n scale\n }\n : undefined;\n}\n\nfunction readVelocity(value: ComponentValue): Velocity | undefined {\n if (!isRecord(value)) {\n return undefined;\n }\n\n const linear = readVector2(value.linear);\n return linear ? { linear } : undefined;\n}\n\nfunction readCollider(value: ComponentValue): Collider | undefined {\n if (!isRecord(value) || value.shape !== \"box\" || typeof value.static !== \"boolean\") {\n return undefined;\n }\n\n const size = readVector2(value.size);\n if (!size) {\n return undefined;\n }\n\n return {\n shape: \"box\",\n size,\n static: value.static,\n ...(typeof value.sensor === \"boolean\" ? { sensor: value.sensor } : {})\n };\n}\n\nfunction readRigidBody(value: ComponentValue): RigidBody | undefined {\n if (!isRecord(value) || ![\"static\", \"dynamic\", \"kinematic\"].includes(String(value.type))) {\n return undefined;\n }\n\n return {\n type: value.type as RigidBody[\"type\"],\n ...(typeof value.gravityScale === \"number\" ? { gravityScale: value.gravityScale } : {}),\n ...(typeof value.lockedRotation === \"boolean\" ? { lockedRotation: value.lockedRotation } : {})\n };\n}\n\nfunction readPhysicsMaterial(value: ComponentValue): PhysicsMaterial | undefined {\n if (!isRecord(value) || typeof value.friction !== \"number\" || typeof value.restitution !== \"number\") {\n return undefined;\n }\n\n return {\n friction: value.friction,\n restitution: value.restitution\n };\n}\n\nfunction readVector2(value: unknown): Vector2 | undefined {\n if (\n !Array.isArray(value) ||\n value.length !== 2 ||\n typeof value[0] !== \"number\" ||\n typeof value[1] !== \"number\" ||\n !Number.isFinite(value[0]) ||\n !Number.isFinite(value[1])\n ) {\n return undefined;\n }\n\n return [value[0], value[1]];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction arraysEqual(left: readonly string[], right: readonly string[]): boolean {\n return left.length === right.length && left.every((value, index) => value === right[index]);\n}\n\nfunction staticBodySummariesEqual(\n left: readonly PhysicsStaticBodySummary[],\n right: readonly PhysicsStaticBodySummary[]\n): boolean {\n if (left.length !== right.length) {\n return false;\n }\n\n return left.every((leftSummary, index) => {\n const rightSummary = right[index];\n if (!rightSummary || leftSummary.entityId !== rightSummary.entityId) {\n return false;\n }\n\n if (!leftSummary.position || !rightSummary.position) {\n return leftSummary.position === rightSummary.position;\n }\n\n return leftSummary.position[0] === rightSummary.position[0] && leftSummary.position[1] === rightSummary.position[1];\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\r\n Collider,\r\n ComponentName,\r\n ComponentValue,\r\n EntityId,\r\n EntityRecord,\r\n GroundedState,\r\n PhysicsJoint,\r\n PhysicsMaterial,\r\n RigidBody,\r\n Transform,\r\n Vector2,\r\n Velocity,\r\n World\r\n} from \"@game_engine/core\";\r\n\r\nexport type PhysicsAdapterSeverity = \"warning\" | \"error\";\r\nexport type PhysicsBodyType = \"static\" | \"dynamic\" | \"kinematic\";\r\n\r\nexport interface PhysicsAdapterDiagnostic {\r\n severity: PhysicsAdapterSeverity;\r\n code: string;\r\n message: string;\r\n entityId?: EntityId;\r\n path?: string;\r\n suggestion?: string;\r\n}\r\n\r\nexport type PhysicsAdapterDiagnostics = PhysicsAdapterDiagnostic[];\r\n\r\nexport interface PhysicsAdapterEntity {\r\n id: EntityId;\r\n components: Record<ComponentName, ComponentValue>;\r\n}\r\n\r\nexport interface PhysicsAdapterWorldInput {\r\n entities: PhysicsAdapterEntity[];\r\n}\r\n\r\nexport interface PhysicsAdapterCreateOptions {\r\n gravity?: Vector2;\r\n}\r\n\r\nexport interface PhysicsStepOptions {\r\n frame?: number;\r\n}\r\n\r\nexport interface PhysicsContactSnapshot {\r\n entityId: EntityId;\r\n otherEntityId: EntityId;\r\n grounded?: boolean;\r\n}\r\n\r\nexport type PhysicsCollisionPhase = \"start\" | \"stay\" | \"end\";\r\n\r\nexport interface PhysicsCollisionEvent {\r\n phase: PhysicsCollisionPhase;\r\n entityAId: EntityId;\r\n entityBId: EntityId;\r\n sensor: boolean;\r\n}\r\n\r\nexport interface PhysicsGroundingSnapshot {\r\n grounded: boolean;\r\n groundEntityId?: EntityId;\r\n}\r\n\r\nexport interface PhysicsBodySnapshot {\r\n entityId: EntityId;\r\n bodyType: PhysicsBodyType;\r\n transform?: Pick<Transform, \"position\" | \"rotation\">;\r\n velocity?: Velocity;\r\n grounded?: PhysicsGroundingSnapshot;\r\n contacts: PhysicsContactSnapshot[];\r\n}\r\n\r\nexport interface PhysicsJointSnapshot {\r\n entityId: EntityId;\r\n type: PhysicsJoint[\"type\"];\r\n bodyAId: EntityId;\r\n bodyBId: EntityId;\r\n}\r\n\r\nexport interface PhysicsQueryFilter {\r\n collisionLayers?: number[];\r\n includeSensors?: boolean;\r\n}\r\n\r\nexport interface PhysicsQueryHit {\r\n entityId: EntityId;\r\n bodyType: PhysicsBodyType;\r\n sensor: boolean;\r\n point?: Vector2;\r\n normal?: Vector2;\r\n fraction?: number;\r\n}\r\n\r\nexport interface PhysicsStepResult {\r\n ok: boolean;\r\n frame: number;\r\n deltaSeconds: number;\r\n bodies: PhysicsBodySnapshot[];\r\n contacts: PhysicsContactSnapshot[];\r\n collisionEvents: PhysicsCollisionEvent[];\r\n joints?: PhysicsJointSnapshot[];\r\n diagnostics: PhysicsAdapterDiagnostics;\r\n}\r\n\r\nexport interface PhysicsAdapterWorld {\r\n step(deltaSeconds: number, options?: PhysicsStepOptions): PhysicsStepResult;\r\n snapshot(options?: PhysicsStepOptions): PhysicsStepResult;\r\n queryPoint?(point: Vector2, filter?: PhysicsQueryFilter): PhysicsQueryHit[];\r\n queryAabb?(lowerBound: Vector2, upperBound: Vector2, filter?: PhysicsQueryFilter): PhysicsQueryHit[];\r\n raycast?(from: Vector2, to: Vector2, filter?: PhysicsQueryFilter): PhysicsQueryHit[];\r\n dispose?(): void;\r\n}\r\n\r\nexport interface PhysicsAdapter {\r\n name: string;\r\n createWorld(\r\n input: PhysicsAdapterWorldInput,\r\n options?: PhysicsAdapterCreateOptions\r\n ): PhysicsAdapterWorld | Promise<PhysicsAdapterWorld>;\r\n}\r\n\r\nexport interface PhysicsComponents {\r\n transform?: Transform;\r\n velocity?: Velocity;\r\n collider?: Collider;\r\n rigidBody?: RigidBody;\r\n physicsMaterial?: PhysicsMaterial;\r\n physicsJoint?: PhysicsJoint;\r\n}\r\n\r\nexport interface PhysicsBackendComparisonInput {\r\n backend: string;\r\n result: PhysicsStepResult;\r\n}\r\n\r\nexport interface PhysicsStaticBodySummary {\r\n entityId: EntityId;\r\n position?: Vector2;\r\n}\r\n\r\nexport interface PhysicsBackendResultSummary {\r\n backend: string;\r\n ok: boolean;\r\n bodyCount: number;\r\n bodyIds: EntityId[];\r\n staticBodies: PhysicsStaticBodySummary[];\r\n diagnosticCodes: string[];\r\n contactPairs: string[];\r\n}\r\n\r\nexport interface PhysicsBackendComparison {\r\n summaries: PhysicsBackendResultSummary[];\r\n sameBodyIds: boolean;\r\n sameStaticBodyPositions: boolean;\r\n diagnosticsShapeCompatible: boolean;\r\n}\r\n\r\nexport function createPhysicsAdapterInputFromWorld(world: World): PhysicsAdapterWorldInput {\r\n return {\r\n entities: world.listEntities().map((entity) => cloneAdapterEntity(entity))\r\n };\r\n}\r\n\r\nexport function applyPhysicsStepResultToWorld(world: World, result: PhysicsStepResult): void {\r\n for (const body of result.bodies) {\r\n const entity = world.getEntity(body.entityId);\r\n if (!entity) {\r\n continue;\r\n }\r\n\r\n const transform = entity.components.Transform as Transform | undefined;\r\n if (transform && body.transform) {\r\n transform.position = [...body.transform.position];\r\n transform.rotation = body.transform.rotation;\r\n }\r\n\r\n const velocity = entity.components.Velocity as Velocity | undefined;\r\n if (velocity && body.velocity) {\r\n velocity.linear = [...body.velocity.linear];\r\n }\r\n\r\n const groundedState = entity.components.GroundedState as GroundedState | undefined;\r\n if (groundedState) {\r\n groundedState.grounded = body.grounded?.grounded === true;\r\n if (body.grounded?.groundEntityId) {\r\n groundedState.groundEntityId = body.grounded.groundEntityId;\r\n } else {\r\n delete groundedState.groundEntityId;\r\n }\r\n }\r\n }\r\n}\r\n\r\nexport function clonePhysicsAdapterInput(input: PhysicsAdapterWorldInput): PhysicsAdapterWorldInput {\r\n return {\r\n entities: input.entities.map((entity) => cloneAdapterEntity(entity))\r\n };\r\n}\r\n\r\nexport function readPhysicsComponents(entity: PhysicsAdapterEntity): PhysicsComponents {\r\n return {\r\n transform: readTransform(entity.components.Transform),\r\n velocity: readVelocity(entity.components.Velocity),\r\n collider: readCollider(entity.components.Collider),\r\n rigidBody: readRigidBody(entity.components.RigidBody),\r\n physicsMaterial: readPhysicsMaterial(entity.components.PhysicsMaterial),\r\n physicsJoint: readPhysicsJoint(entity.components.PhysicsJoint)\r\n };\r\n}\r\n\r\nexport function resolvePhysicsBodyType(components: PhysicsComponents): PhysicsBodyType {\r\n if (components.rigidBody) {\r\n return components.rigidBody.type;\r\n }\r\n\r\n return components.collider?.static ? \"static\" : \"dynamic\";\r\n}\r\n\r\nexport function sortPhysicsBodySnapshots(bodies: PhysicsBodySnapshot[]): PhysicsBodySnapshot[] {\r\n return [...bodies].sort((left, right) => left.entityId.localeCompare(right.entityId));\r\n}\r\n\r\nexport function sortPhysicsContacts(contacts: PhysicsContactSnapshot[]): PhysicsContactSnapshot[] {\r\n return [...contacts].sort((left, right) => {\r\n const entityOrder = left.entityId.localeCompare(right.entityId);\r\n return entityOrder !== 0 ? entityOrder : left.otherEntityId.localeCompare(right.otherEntityId);\r\n });\r\n}\r\n\r\nexport function sortPhysicsCollisionEvents(events: PhysicsCollisionEvent[]): PhysicsCollisionEvent[] {\r\n const phaseOrder: Record<PhysicsCollisionPhase, number> = { start: 0, stay: 1, end: 2 };\r\n return [...events].sort((left, right) => {\r\n const entityAOrder = left.entityAId.localeCompare(right.entityAId);\r\n if (entityAOrder !== 0) {\r\n return entityAOrder;\r\n }\r\n\r\n const entityBOrder = left.entityBId.localeCompare(right.entityBId);\r\n return entityBOrder !== 0 ? entityBOrder : phaseOrder[left.phase] - phaseOrder[right.phase];\r\n });\r\n}\r\n\r\nexport function sortPhysicsJointSnapshots(joints: PhysicsJointSnapshot[]): PhysicsJointSnapshot[] {\r\n return [...joints].sort((left, right) => left.entityId.localeCompare(right.entityId));\r\n}\r\n\r\nexport function sortPhysicsQueryHits(hits: PhysicsQueryHit[]): PhysicsQueryHit[] {\r\n return [...hits].sort((left, right) => {\r\n const fractionOrder = (left.fraction ?? 0) - (right.fraction ?? 0);\r\n return fractionOrder !== 0 ? fractionOrder : left.entityId.localeCompare(right.entityId);\r\n });\r\n}\r\n\r\nexport function createPhysicsStepResult(\r\n fields: Omit<PhysicsStepResult, \"ok\" | \"bodies\" | \"contacts\" | \"collisionEvents\" | \"diagnostics\"> & {\r\n bodies?: PhysicsBodySnapshot[];\r\n contacts?: PhysicsContactSnapshot[];\r\n collisionEvents?: PhysicsCollisionEvent[];\r\n diagnostics?: PhysicsAdapterDiagnostics;\r\n }\r\n): PhysicsStepResult {\r\n const bodies = sortPhysicsBodySnapshots(fields.bodies ?? []);\r\n const contacts = sortPhysicsContacts(fields.contacts ?? []);\r\n const collisionEvents = sortPhysicsCollisionEvents(fields.collisionEvents ?? []);\r\n const joints = sortPhysicsJointSnapshots(fields.joints ?? []);\r\n const diagnostics = sortDiagnostics(fields.diagnostics ?? []);\r\n\r\n return {\r\n ok: !diagnostics.some((diagnostic) => diagnostic.severity === \"error\"),\r\n frame: fields.frame,\r\n deltaSeconds: roundDeterministic(fields.deltaSeconds),\r\n bodies,\r\n contacts,\r\n collisionEvents,\r\n ...(fields.joints ? { joints } : {}),\r\n diagnostics\r\n };\r\n}\r\n\r\nexport function createPhysicsDiagnostic(\r\n diagnostic: PhysicsAdapterDiagnostic\r\n): PhysicsAdapterDiagnostic {\r\n return { ...diagnostic };\r\n}\r\n\r\nexport function summarizePhysicsStepResult(\r\n backend: string,\r\n result: PhysicsStepResult\r\n): PhysicsBackendResultSummary {\r\n return {\r\n backend,\r\n ok: result.ok,\r\n bodyCount: result.bodies.length,\r\n bodyIds: result.bodies.map((body) => body.entityId).sort(),\r\n staticBodies: result.bodies\r\n .filter((body) => body.bodyType === \"static\")\r\n .map((body) => ({\r\n entityId: body.entityId,\r\n ...(body.transform?.position ? { position: roundVector2(body.transform.position) } : {})\r\n }))\r\n .sort((left, right) => left.entityId.localeCompare(right.entityId)),\r\n diagnosticCodes: result.diagnostics.map((diagnostic) => diagnostic.code).sort(),\r\n contactPairs: result.contacts\r\n .map((contact) => `${contact.entityId}->${contact.otherEntityId}`)\r\n .sort()\r\n };\r\n}\r\n\r\nexport function comparePhysicsStepResults(\r\n inputs: PhysicsBackendComparisonInput[]\r\n): PhysicsBackendComparison {\r\n const summaries = inputs\r\n .map((input) => summarizePhysicsStepResult(input.backend, input.result))\r\n .sort((left, right) => left.backend.localeCompare(right.backend));\r\n const [baseline] = summaries;\r\n\r\n return {\r\n summaries,\r\n sameBodyIds: baseline ? summaries.every((summary) => arraysEqual(summary.bodyIds, baseline.bodyIds)) : true,\r\n sameStaticBodyPositions: baseline\r\n ? summaries.every((summary) => staticBodySummariesEqual(summary.staticBodies, baseline.staticBodies))\r\n : true,\r\n diagnosticsShapeCompatible: baseline\r\n ? summaries.every((summary) => arraysEqual(summary.diagnosticCodes, baseline.diagnosticCodes))\r\n : true\r\n };\r\n}\r\n\r\nexport function roundDeterministic(value: number): number {\r\n const rounded = Number(value.toFixed(12));\r\n const nearestInteger = Math.round(rounded);\r\n return Math.abs(rounded - nearestInteger) < 1e-9 ? nearestInteger : rounded;\r\n}\r\n\r\nexport function roundVector2(value: Vector2): Vector2 {\r\n return [roundDeterministic(value[0]), roundDeterministic(value[1])];\r\n}\r\n\r\nfunction cloneAdapterEntity(entity: EntityRecord | PhysicsAdapterEntity): PhysicsAdapterEntity {\r\n return {\r\n id: entity.id,\r\n components: cloneComponents(entity.components)\r\n };\r\n}\r\n\r\nfunction cloneComponents(components: Record<ComponentName, ComponentValue>): Record<ComponentName, ComponentValue> {\r\n return Object.fromEntries(Object.entries(components).map(([name, value]) => [name, cloneValue(value)]));\r\n}\r\n\r\nfunction cloneValue(value: ComponentValue): ComponentValue {\r\n if (value === undefined) {\r\n return undefined;\r\n }\r\n\r\n return JSON.parse(JSON.stringify(value));\r\n}\r\n\r\nfunction sortDiagnostics(diagnostics: PhysicsAdapterDiagnostics): PhysicsAdapterDiagnostics {\r\n return [...diagnostics].sort((left, right) => {\r\n const entityOrder = (left.entityId ?? \"\").localeCompare(right.entityId ?? \"\");\r\n if (entityOrder !== 0) {\r\n return entityOrder;\r\n }\r\n\r\n const pathOrder = (left.path ?? \"\").localeCompare(right.path ?? \"\");\r\n return pathOrder !== 0 ? pathOrder : left.code.localeCompare(right.code);\r\n });\r\n}\r\n\r\nfunction readTransform(value: ComponentValue): Transform | undefined {\r\n if (!isRecord(value)) {\r\n return undefined;\r\n }\r\n\r\n const position = readVector2(value.position);\r\n const scale = readVector2(value.scale);\r\n const rotation = typeof value.rotation === \"number\" && Number.isFinite(value.rotation) ? value.rotation : undefined;\r\n\r\n return position && scale && rotation !== undefined\r\n ? {\r\n position,\r\n rotation,\r\n scale\r\n }\r\n : undefined;\r\n}\r\n\r\nfunction readVelocity(value: ComponentValue): Velocity | undefined {\r\n if (!isRecord(value)) {\r\n return undefined;\r\n }\r\n\r\n const linear = readVector2(value.linear);\r\n return linear ? { linear } : undefined;\r\n}\r\n\r\nfunction readCollider(value: ComponentValue): Collider | undefined {\r\n if (!isRecord(value) || value.shape !== \"box\" || typeof value.static !== \"boolean\") {\r\n return undefined;\r\n }\r\n\r\n const size = readVector2(value.size);\r\n if (!size) {\r\n return undefined;\r\n }\r\n\r\n return {\r\n shape: \"box\",\r\n size,\r\n static: value.static,\r\n ...(typeof value.sensor === \"boolean\" ? { sensor: value.sensor } : {}),\r\n ...(typeof value.collisionLayer === \"number\" ? { collisionLayer: value.collisionLayer } : {}),\r\n ...(Array.isArray(value.collisionMask) && value.collisionMask.every((layer) => typeof layer === \"number\")\r\n ? { collisionMask: [...value.collisionMask] as number[] }\r\n : {})\r\n };\r\n}\r\n\r\nfunction readRigidBody(value: ComponentValue): RigidBody | undefined {\r\n if (!isRecord(value) || ![\"static\", \"dynamic\", \"kinematic\"].includes(String(value.type))) {\r\n return undefined;\r\n }\r\n\r\n return {\r\n type: value.type as RigidBody[\"type\"],\r\n ...(typeof value.gravityScale === \"number\" ? { gravityScale: value.gravityScale } : {}),\r\n ...(typeof value.lockedRotation === \"boolean\" ? { lockedRotation: value.lockedRotation } : {}),\r\n ...(typeof value.continuousCollisionDetection === \"boolean\"\r\n ? { continuousCollisionDetection: value.continuousCollisionDetection }\r\n : {})\r\n };\r\n}\r\n\r\nfunction readPhysicsMaterial(value: ComponentValue): PhysicsMaterial | undefined {\r\n if (!isRecord(value) || typeof value.friction !== \"number\" || typeof value.restitution !== \"number\") {\r\n return undefined;\r\n }\r\n\r\n return {\r\n friction: value.friction,\r\n restitution: value.restitution\r\n };\r\n}\r\n\r\nfunction readPhysicsJoint(value: ComponentValue): PhysicsJoint | undefined {\r\n if (!isRecord(value) || typeof value.bodyA !== \"string\" || typeof value.bodyB !== \"string\") {\r\n return undefined;\r\n }\r\n\r\n if (value.type === \"distance\") {\r\n const anchorA = readVector2(value.anchorA);\r\n const anchorB = readVector2(value.anchorB);\r\n if (!anchorA || !anchorB) {\r\n return undefined;\r\n }\r\n return {\r\n type: \"distance\",\r\n bodyA: value.bodyA,\r\n bodyB: value.bodyB,\r\n anchorA,\r\n anchorB,\r\n ...(typeof value.length === \"number\" ? { length: value.length } : {}),\r\n ...(typeof value.frequencyHz === \"number\" ? { frequencyHz: value.frequencyHz } : {}),\r\n ...(typeof value.dampingRatio === \"number\" ? { dampingRatio: value.dampingRatio } : {}),\r\n ...(typeof value.collideConnected === \"boolean\" ? { collideConnected: value.collideConnected } : {})\r\n };\r\n }\r\n\r\n if (value.type === \"revolute\") {\r\n const anchor = readVector2(value.anchor);\r\n if (!anchor) {\r\n return undefined;\r\n }\r\n return {\r\n type: \"revolute\",\r\n bodyA: value.bodyA,\r\n bodyB: value.bodyB,\r\n anchor,\r\n ...(typeof value.enableLimit === \"boolean\" ? { enableLimit: value.enableLimit } : {}),\r\n ...(typeof value.lowerAngle === \"number\" ? { lowerAngle: value.lowerAngle } : {}),\r\n ...(typeof value.upperAngle === \"number\" ? { upperAngle: value.upperAngle } : {}),\r\n ...(typeof value.enableMotor === \"boolean\" ? { enableMotor: value.enableMotor } : {}),\r\n ...(typeof value.motorSpeed === \"number\" ? { motorSpeed: value.motorSpeed } : {}),\r\n ...(typeof value.maxMotorTorque === \"number\" ? { maxMotorTorque: value.maxMotorTorque } : {}),\r\n ...(typeof value.collideConnected === \"boolean\" ? { collideConnected: value.collideConnected } : {})\r\n };\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nfunction readVector2(value: unknown): Vector2 | undefined {\r\n if (\r\n !Array.isArray(value) ||\r\n value.length !== 2 ||\r\n typeof value[0] !== \"number\" ||\r\n typeof value[1] !== \"number\" ||\r\n !Number.isFinite(value[0]) ||\r\n !Number.isFinite(value[1])\r\n ) {\r\n return undefined;\r\n }\r\n\r\n return [value[0], value[1]];\r\n}\r\n\r\nfunction isRecord(value: unknown): value is Record<string, unknown> {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\r\n}\r\n\r\nfunction arraysEqual(left: readonly string[], right: readonly string[]): boolean {\r\n return left.length === right.length && left.every((value, index) => value === right[index]);\r\n}\r\n\r\nfunction staticBodySummariesEqual(\r\n left: readonly PhysicsStaticBodySummary[],\r\n right: readonly PhysicsStaticBodySummary[]\r\n): boolean {\r\n if (left.length !== right.length) {\r\n return false;\r\n }\r\n\r\n return left.every((leftSummary, index) => {\r\n const rightSummary = right[index];\r\n if (!rightSummary || leftSummary.entityId !== rightSummary.entityId) {\r\n return false;\r\n }\r\n\r\n if (!leftSummary.position || !rightSummary.position) {\r\n return leftSummary.position === rightSummary.position;\r\n }\r\n\r\n return leftSummary.position[0] === rightSummary.position[0] && leftSummary.position[1] === rightSummary.position[1];\r\n });\r\n}\r\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@game_engine/physics-adapter",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "Backend-neutral physics adapter contracts for Agent Engine experiments.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -21,6 +21,9 @@
21
21
  "publishConfig": {
22
22
  "access": "public"
23
23
  },
24
+ "engines": {
25
+ "node": ">=22.11.0 <23 || >=24.11.0 <25"
26
+ },
24
27
  "type": "module",
25
28
  "main": "./dist/index.js",
26
29
  "types": "./dist/index.d.ts",
@@ -31,7 +34,7 @@
31
34
  }
32
35
  },
33
36
  "dependencies": {
34
- "@game_engine/core": "0.1.0-alpha.1"
37
+ "@game_engine/core": "0.1.0-alpha.2"
35
38
  },
36
39
  "devDependencies": {
37
40
  "tsup": "^8.3.5",