@adatechnology/game-shapes 0.1.0 → 0.2.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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as ShapeSchema, G as GenerateOptions, a as ShapeBuffers, b as ShapeNode, P as PrimitiveNode, c as PrimitiveBuffers, d as ShapeOutline, B as BevelSpec, V as Vec3, H as HoleSpec, e as ShapeGenerator, C as Color, T as Transform, R as RotationOrder } from './worker-W5A14FiP.js';
2
- export { f as Bounds, g as BoxNode, h as CircleNode, i as ConeNode, j as CustomNode, k as CylinderNode, D as DifferenceNode, E as ExtrudeNode, l as GeneratorContext, m as GeometryGroup, n as GroupNode, I as InstanceSpec, o as InstancedBatch, L as LatheNode, p as LodLevel, M as Mat4, q as MaterialHint, r as MeshNode, N as NamedRange, s as NodeCommon, t as PlaneNode, u as PointNode, v as RingNode, w as ShapeBatch, x as ShapeJob, y as ShapeKind, z as ShapeLevel, A as ShapePart, F as ShapePoint, J as ShapeReply, K as ShapeStats, O as ShapeWorkerOptions, Q as SphereNode, U as TorusNode, W as UvRect, X as Vec2, Y as WorkerScopeLike, Z as installShapeWorker } from './worker-W5A14FiP.js';
1
+ import { S as ShapeSchema, G as GenerateOptions, a as ShapeBuffers, b as ShapeNode, P as PrimitiveNode, c as PrimitiveBuffers, d as ShapeOutline, B as BevelSpec, V as Vec3, H as HoleSpec, e as ShapeGenerator, C as Color, T as Transform, R as RotationOrder } from './worker-BZ7plgyI.js';
2
+ export { f as Bounds, g as BoxNode, h as CircleNode, i as ConeNode, j as CustomNode, k as CylinderNode, D as DifferenceNode, E as ExtrudeNode, l as GeneratorContext, m as GeometryGroup, n as GroupNode, I as InstanceSpec, o as InstancedBatch, L as LatheNode, p as LodLevel, M as Mat4, q as MaterialHint, r as MeshNode, N as NamedRange, s as NodeCommon, t as PlaneNode, u as PointNode, v as RingNode, w as ShapeBatch, x as ShapeJob, y as ShapeKind, z as ShapeLevel, A as ShapePart, F as ShapePoint, J as ShapeReply, K as ShapeStats, O as ShapeWorkerOptions, Q as SphereNode, U as TorusNode, W as UvRect, X as Vec2, Y as WorkerScopeLike, Z as installShapeWorker } from './worker-BZ7plgyI.js';
3
3
 
4
4
  declare const isPrimitiveNode: (node: ShapeNode) => node is PrimitiveNode;
5
5
  /** The options that change the output, as they enter the cache key. */
@@ -93,15 +93,45 @@ declare function validateShape(schema: unknown): string[];
93
93
  /** Throws with every problem when the schema is not valid. */
94
94
  declare function assertValidShape(schema: unknown): asserts schema is ShapeSchema;
95
95
 
96
- /**
97
- * Registers a generator for `{ kind: 'custom', generator: name, params }` nodes: a pure function of its params and a seeded stream that returns a
98
- * node. A worker that generates shapes with custom nodes must register the same generators (import the same module in the worker script).
99
- */
100
- declare function registerGenerator(name: string, generator: ShapeGenerator): void;
96
+ /** The generators of one set: what `{ kind: 'custom', generator: name, params }` nodes look up. */
97
+ interface ShapeGenerators {
98
+ /**
99
+ * Registers a generator: a pure function of its params and a seeded stream that returns a node. A worker that generates shapes with custom nodes must
100
+ * register the same generators (import the same module in the worker script).
101
+ */
102
+ register(name: string, generator: ShapeGenerator): void;
103
+ unregister(name: string): boolean;
104
+ get(name: string): ShapeGenerator | undefined;
105
+ list(): string[];
106
+ /** The generators by name, as `GenerateOptions.generators` takes them. */
107
+ record(): Readonly<Record<string, ShapeGenerator>>;
108
+ }
109
+ /** The set behind `registerGenerator`, `getGenerator` and the rest of the global functions, created on first use. */
110
+ declare const defaultShapeGenerators: () => ShapeGenerators;
111
+ /** @deprecated use `createShapes(...).register`. */
112
+ declare const registerGenerator: (name: string, generator: ShapeGenerator) => void;
113
+ /** @deprecated use `createShapes(...).unregister`. */
101
114
  declare const unregisterGenerator: (name: string) => boolean;
115
+ /** @deprecated use `createShapes(...).get`. */
102
116
  declare const getGenerator: (name: string) => ShapeGenerator | undefined;
117
+ /** @deprecated use `createShapes(...).list`. */
103
118
  declare const listGenerators: () => string[];
104
119
 
120
+ interface ShapesOptions {
121
+ /** Generators by name, added to the set; later packs replace earlier ones with the same name. */
122
+ readonly packs?: readonly Readonly<Record<string, ShapeGenerator>>[];
123
+ /** A generator replaced by name (a function is replaced as a whole). */
124
+ readonly patch?: Readonly<Record<string, ShapeGenerator>>;
125
+ /** Names taken out. */
126
+ readonly remove?: readonly string[];
127
+ }
128
+ interface Shapes extends ShapeGenerators {
129
+ /** `generate` that looks custom nodes up in this set only (a name only the global registry has throws). */
130
+ generate(schema: ShapeSchema, options?: Omit<GenerateOptions, 'isolatedGenerators'>): ShapeBuffers;
131
+ }
132
+ /** A set of shape generators of its own. Nothing is shared with any other set or with the global registry. */
133
+ declare function createShapes(options?: ShapesOptions): Shapes;
134
+
105
135
  /**
106
136
  * Canonical JSON (keys sorted, `undefined` left out, `-0` kept as `-0`, typed arrays as plain lists) and a 64-bit FNV-1a of it in two 32-bit
107
137
  * lanes: the key a shape is cached and shared by.
@@ -190,6 +220,8 @@ interface ShapePoolOptions {
190
220
  readonly cache?: ShapeCache;
191
221
  /** Generators of the `custom` nodes for the jobs made in the calling thread (the workers register their own). */
192
222
  readonly generators?: Readonly<Record<string, ShapeGenerator>>;
223
+ /** Look custom nodes up in `generators` only (for example `shapes.record()` of `createShapes`), never in the global registry; the jobs then run in the calling thread, since a worker cannot receive functions. */
224
+ readonly isolatedGenerators?: boolean;
193
225
  }
194
226
  interface ShapeRequest {
195
227
  readonly schema: ShapeSchema;
@@ -218,4 +250,4 @@ interface ShapePool {
218
250
  */
219
251
  declare function createShapePool(options?: ShapePoolOptions): ShapePool;
220
252
 
221
- export { BevelSpec, Color, type DetailOptions, GenerateOptions, HoleSpec, type Matrix3, type Matrix4, type MergedArrays, PrimitiveBuffers, PrimitiveNode, RotationOrder, SHAPE_SCHEMA, ShapeBuffers, type ShapeCache, type ShapeCacheOptions, type ShapeCacheStats, ShapeGenerator, ShapeNode, ShapeOutline, type ShapePool, type ShapePoolOptions, type ShapePoolStats, type ShapeRequest, ShapeSchema, Transform, type TransformedPiece, Vec3, type WorkerLike, assertValidShape, box, buildPrimitive, byteSizeOf, canonicalJson, circle, clearTemplateCache, cloneBuffers, clonePrimitive, colorChannels, colorHex, compose, computeVertexNormals, cone, createShapeCache, createShapePool, cylinder, difference, extrude, generate, getGenerator, hash64, hashValue, headingMatrix, identity4, isAffine, isPrimitiveNode, keyOptions, lathe, listGenerators, matrixOf, mergeTransformed, mulberry32, multiply, normalMatrix, plane, primitiveParameters, quaternionFromEuler, registerGenerator, ring, setTemplateCacheSize, shapeKey, sphere, srgbToLinear, streamFor, templateCacheStats, torus, transferablesOf, transformPoint, unregisterGenerator, validateShape };
253
+ export { BevelSpec, Color, type DetailOptions, GenerateOptions, HoleSpec, type Matrix3, type Matrix4, type MergedArrays, PrimitiveBuffers, PrimitiveNode, RotationOrder, SHAPE_SCHEMA, ShapeBuffers, type ShapeCache, type ShapeCacheOptions, type ShapeCacheStats, ShapeGenerator, type ShapeGenerators, ShapeNode, ShapeOutline, type ShapePool, type ShapePoolOptions, type ShapePoolStats, type ShapeRequest, ShapeSchema, type Shapes, type ShapesOptions, Transform, type TransformedPiece, Vec3, type WorkerLike, assertValidShape, box, buildPrimitive, byteSizeOf, canonicalJson, circle, clearTemplateCache, cloneBuffers, clonePrimitive, colorChannels, colorHex, compose, computeVertexNormals, cone, createShapeCache, createShapePool, createShapes, cylinder, defaultShapeGenerators, difference, extrude, generate, getGenerator, hash64, hashValue, headingMatrix, identity4, isAffine, isPrimitiveNode, keyOptions, lathe, listGenerators, matrixOf, mergeTransformed, mulberry32, multiply, normalMatrix, plane, primitiveParameters, quaternionFromEuler, registerGenerator, ring, setTemplateCacheSize, shapeKey, sphere, srgbToLinear, streamFor, templateCacheStats, torus, transferablesOf, transformPoint, unregisterGenerator, validateShape };
@@ -40,7 +40,9 @@ var GameShapes = (() => {
40
40
  cone: () => cone,
41
41
  createShapeCache: () => createShapeCache,
42
42
  createShapePool: () => createShapePool,
43
+ createShapes: () => createShapes,
43
44
  cylinder: () => cylinder,
45
+ defaultShapeGenerators: () => defaultShapeGenerators,
44
46
  difference: () => difference,
45
47
  extrude: () => extrude,
46
48
  generate: () => generate,
@@ -1596,14 +1598,23 @@ var GameShapes = (() => {
1596
1598
  var streamFor = (seed, generator, path) => mulberry32(parseInt(hash64(`${String(seed)}\0${generator}\0${path}`).slice(0, 8), 16));
1597
1599
 
1598
1600
  // src/registry.ts
1599
- var generators = /* @__PURE__ */ new Map();
1600
- function registerGenerator(name, generator) {
1601
- if (!/^[a-zA-Z][\w.:-]*$/.test(name)) throw new Error(`game-shapes: generator name "${name}" must start with a letter and use letters, digits, ".", ":", "-" or "_"`);
1602
- generators.set(name, generator);
1603
- }
1604
- var unregisterGenerator = (name) => generators.delete(name);
1605
- var getGenerator = (name) => generators.get(name);
1606
- var listGenerators = () => [...generators.keys()].sort();
1601
+ var GENERATOR_NAME = /^[a-zA-Z][\w.:-]*$/;
1602
+ var generatorsOver = (generators) => ({
1603
+ register(name, generator) {
1604
+ if (!GENERATOR_NAME.test(name)) throw new Error(`game-shapes: generator name "${name}" must start with a letter and use letters, digits, ".", ":", "-" or "_"`);
1605
+ generators.set(name, generator);
1606
+ },
1607
+ unregister: (name) => generators.delete(name),
1608
+ get: (name) => generators.get(name),
1609
+ list: () => [...generators.keys()].sort(),
1610
+ record: () => Object.fromEntries(generators)
1611
+ });
1612
+ var defaultGenerators;
1613
+ var defaultShapeGenerators = () => defaultGenerators ?? (defaultGenerators = generatorsOver(/* @__PURE__ */ new Map()));
1614
+ var registerGenerator = (name, generator) => defaultShapeGenerators().register(name, generator);
1615
+ var unregisterGenerator = (name) => defaultShapeGenerators().unregister(name);
1616
+ var getGenerator = (name) => defaultShapeGenerators().get(name);
1617
+ var listGenerators = () => defaultShapeGenerators().list();
1607
1618
 
1608
1619
  // schema/shape.schema.json
1609
1620
  var shape_schema_default = {
@@ -2649,7 +2660,7 @@ var GameShapes = (() => {
2649
2660
  return r * Math.max(sx, sy, sz);
2650
2661
  };
2651
2662
  function expandCustom(node, settings, detail, path) {
2652
- const generator = settings.generators?.[node.generator] ?? getGenerator(node.generator);
2663
+ const generator = settings.generators?.[node.generator] ?? (settings.isolatedGenerators ? void 0 : getGenerator(node.generator));
2653
2664
  if (!generator) throw new Error(`game-shapes: no generator "${node.generator}" (register it with registerGenerator, in the worker too) at ${path}`);
2654
2665
  const generated = generator(node.params, { random: streamFor(settings.seed, node.generator, path), seed: settings.seed, detail, path });
2655
2666
  const { kind: _kind, generator: _generator, params: _params, ...common } = node;
@@ -2857,6 +2868,7 @@ var GameShapes = (() => {
2857
2868
  ...options.materialAlias ? { alias: options.materialAlias } : {},
2858
2869
  seed: options.seed ?? 0,
2859
2870
  ...options.generators ? { generators: options.generators } : {},
2871
+ ...options.isolatedGenerators ? { isolatedGenerators: true } : {},
2860
2872
  ...options.instancing ? { autoMin: Math.max(2, options.instancing.minCount) } : {}
2861
2873
  };
2862
2874
  const specs = [{ distance: 0, detail: 1, index: 0 }, ...(schema.lod ?? []).map((rule, index) => ({ ...rule, index: index + 1 }))];
@@ -2976,6 +2988,118 @@ var GameShapes = (() => {
2976
2988
  return transferablesOf(buffers).reduce((sum, buffer) => sum + buffer.byteLength, 0);
2977
2989
  }
2978
2990
 
2991
+ // ../game-contracts/dist/index.js
2992
+ var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
2993
+ var DEFERRED_QUEUE_ID = {
2994
+ DESTRUCTION_EVENTS: "destruction.events",
2995
+ BREACH_PAINT: "floorplan.breach-paint",
2996
+ AUDIO_VOICES: "audio.voices",
2997
+ VEHICLE_CREASE: "vehicles.crease"
2998
+ };
2999
+ var DEFERRED_PRIORITY = {
3000
+ [DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
3001
+ [DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
3002
+ [DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
3003
+ [DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
3004
+ };
3005
+ var WARNED_KEY = /* @__PURE__ */ Symbol.for("@adatechnology/game-contracts.warned");
3006
+ var warnedKeys = () => {
3007
+ const holder = globalThis;
3008
+ return holder[WARNED_KEY] ?? (holder[WARNED_KEY] = /* @__PURE__ */ new Set());
3009
+ };
3010
+ var isProduction = () => {
3011
+ try {
3012
+ return process.env.NODE_ENV === "production";
3013
+ } catch {
3014
+ return false;
3015
+ }
3016
+ };
3017
+ var isQuiet = () => globalThis.__GAME_KIT_QUIET__ === true;
3018
+ var warnOnce = (key, message) => {
3019
+ if (isQuiet() || isProduction()) return;
3020
+ const seen = warnedKeys();
3021
+ if (seen.has(key)) return;
3022
+ seen.add(key);
3023
+ console.warn(`[game-kit] ${message}`);
3024
+ };
3025
+ var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
3026
+ var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
3027
+ var deepMerge = (base, patch) => {
3028
+ if (!patch) return base;
3029
+ const result = { ...base };
3030
+ for (const [key, value] of Object.entries(patch)) {
3031
+ if (value === void 0 || UNSAFE_KEYS.has(key)) continue;
3032
+ const current = result[key];
3033
+ result[key] = isPlainObject(current) && isPlainObject(value) ? deepMerge(current, value) : value;
3034
+ }
3035
+ return result;
3036
+ };
3037
+ var mergeById = (shipped, extra) => {
3038
+ const entries = /* @__PURE__ */ new Map();
3039
+ for (const entry of [...shipped, ...extra ?? []]) entries.set(entry.id, entry);
3040
+ return entries;
3041
+ };
3042
+ var applyPatches = (entries, patches) => {
3043
+ for (const [id, patch] of Object.entries(patches ?? {})) {
3044
+ const entry = entries.get(id);
3045
+ if (entry) entries.set(id, deepMerge(entry, patch));
3046
+ else warnOnce(`registry.patch.${id}`, `patch for "${id}" ignored: no entry has that id`);
3047
+ }
3048
+ };
3049
+ var removeIds = (entries, ids) => {
3050
+ for (const id of ids ?? []) {
3051
+ if (!entries.delete(id)) warnOnce(`registry.remove.${id}`, `remove of "${id}" ignored: no entry has that id`);
3052
+ }
3053
+ };
3054
+ var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
3055
+ var GAIT_IDS = ["walk", "run"];
3056
+ var LANDING_IDS = ["light", "heavy"];
3057
+ var BODY_SOUND_IDS = [
3058
+ "jump",
3059
+ "cloth_rustle",
3060
+ "body_fall",
3061
+ "fall_damage",
3062
+ "player_hurt",
3063
+ "player_breath_run",
3064
+ "player_death",
3065
+ "bite",
3066
+ "grab",
3067
+ "punch_swing",
3068
+ "punch_hit_body",
3069
+ "punch_hit_face",
3070
+ "punch_hit_object",
3071
+ "kick_swing",
3072
+ "kick_hit",
3073
+ "kick_hit_object",
3074
+ "shove_two_hands",
3075
+ "knife_swing",
3076
+ "knife_stab",
3077
+ "knife_slash",
3078
+ "knife_hit_object",
3079
+ "blunt_swing",
3080
+ "blunt_hit",
3081
+ "blunt_hit_object",
3082
+ "axe_swing",
3083
+ "axe_chop",
3084
+ "axe_hit_object"
3085
+ ];
3086
+ var footstepSoundId = (surface, gait) => `footstep_${surface}_${gait}`;
3087
+ var FOOTSTEP_SOUND_IDS = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)));
3088
+ var LAND_SOUND_IDS = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing) => `land_${surface}_${landing}`));
3089
+ var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
3090
+
3091
+ // src/instance.ts
3092
+ function createShapes(options = {}) {
3093
+ const entryOf = ([id, generator]) => ({ id, generator });
3094
+ const entries = mergeById([], (options.packs ?? []).flatMap((pack2) => Object.entries(pack2).map(entryOf)));
3095
+ const patches = {};
3096
+ for (const [id, generator] of Object.entries(options.patch ?? {})) patches[id] = { generator };
3097
+ applyPatches(entries, patches);
3098
+ removeIds(entries, options.remove);
3099
+ const generators = generatorsOver(new Map([...entries].map(([id, entry]) => [id, entry.generator])));
3100
+ return { ...generators, generate: (schema, generateOptions = {}) => generate(schema, { ...generateOptions, generators: { ...generators.record(), ...generateOptions.generators }, isolatedGenerators: true }) };
3101
+ }
3102
+
2979
3103
  // src/cache.ts
2980
3104
  function createShapeCache(options = {}) {
2981
3105
  const maxEntries = options.maxEntries ?? 256;
@@ -3063,11 +3187,11 @@ var GameShapes = (() => {
3063
3187
  };
3064
3188
  for (let i = 0; i < size; i++) slots.push(spawnSlot());
3065
3189
  const run = (schema, generateOptions) => {
3066
- if (slots.length === 0 || generateOptions.generators) {
3190
+ if (slots.length === 0 || generateOptions.generators || options.isolatedGenerators) {
3067
3191
  inThread++;
3068
3192
  try {
3069
- const generators2 = { ...options.generators ?? {}, ...generateOptions.generators ?? {} };
3070
- return Promise.resolve(generate(schema, { ...generateOptions, ...Object.keys(generators2).length ? { generators: generators2 } : {} }));
3193
+ const generators = { ...options.generators ?? {}, ...generateOptions.generators ?? {} };
3194
+ return Promise.resolve(generate(schema, { ...generateOptions, ...Object.keys(generators).length ? { generators } : {}, ...options.isolatedGenerators ? { isolatedGenerators: true } : {} }));
3071
3195
  } catch (error) {
3072
3196
  return Promise.reject(error);
3073
3197
  }