@woosh/meep-engine 2.82.0 → 2.84.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.
@@ -85688,6 +85688,12 @@ class RingBuffer {
85688
85688
  * @param {number} new_size
85689
85689
  */
85690
85690
  resize(new_size) {
85691
+
85692
+ if (new_size === this.size) {
85693
+ // already the right size
85694
+ return;
85695
+ }
85696
+
85691
85697
  const array = new Array(new_size);
85692
85698
 
85693
85699
  this.data = array;
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "description": "Fully featured ECS game engine written in JavaScript",
6
6
  "type": "module",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.82.0",
8
+ "version": "2.84.0",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -48,6 +48,12 @@ export class RingBuffer {
48
48
  * @param {number} new_size
49
49
  */
50
50
  resize(new_size) {
51
+
52
+ if (new_size === this.size) {
53
+ // already the right size
54
+ return;
55
+ }
56
+
51
57
  const array = new Array(new_size);
52
58
 
53
59
  this.data = array;
@@ -6,9 +6,10 @@ import { bvh_query_leaves_ray } from "../../../../../core/bvh2/bvh3/query/bvh_qu
6
6
  import {
7
7
  bvh_query_user_data_overlaps_frustum
8
8
  } from "../../../../../core/bvh2/bvh3/query/bvh_query_user_data_overlaps_frustum.js";
9
- import { AsyncLoadingCache } from "../../../../../core/collection/map/AsyncLoadingCache.js";
9
+ import { LoadingCache } from "../../../../../core/cache/LoadingCache.js";
10
10
 
11
11
  import { returnTrue } from "../../../../../core/function/returnTrue.js";
12
+ import { strictEquals } from "../../../../../core/function/strictEquals.js";
12
13
  import { aabb3_matrix4_project } from "../../../../../core/geom/3d/aabb/aabb3_matrix4_project.js";
13
14
  import { aabb3_raycast } from "../../../../../core/geom/3d/aabb/aabb3_raycast.js";
14
15
  import {
@@ -17,6 +18,7 @@ import {
17
18
  import { ray3_array_apply_matrix4 } from "../../../../../core/geom/3d/ray/ray3_array_apply_matrix4.js";
18
19
  import { ray3_array_compose } from "../../../../../core/geom/3d/ray/ray3_array_compose.js";
19
20
  import { SurfacePoint3 } from "../../../../../core/geom/3d/SurfacePoint3.js";
21
+ import { computeStringHash } from "../../../../../core/primitives/strings/computeStringHash.js";
20
22
  import { AssetManager } from "../../../../asset/AssetManager.js";
21
23
  import { GameAssetType } from "../../../../asset/GameAssetType.js";
22
24
  import { AbstractContextSystem } from "../../../../ecs/system/AbstractContextSystem.js";
@@ -241,15 +243,19 @@ export class FPDecalSystem extends AbstractContextSystem {
241
243
 
242
244
  /**
243
245
  *
244
- * @type {Map<string, Sampler2D>}
246
+ * @type {LoadingCache<string, Sampler2D>}
245
247
  * @private
246
248
  */
247
- this.__texture_cache = new AsyncLoadingCache(new Map(), (key) => {
248
- return this.__assets.promise(key, GameAssetType.Image).then(asset => {
249
- const asset_sampler = asset.create();
249
+ this.__texture_cache = new LoadingCache({
250
+ load: (key) => {
251
+ return this.__assets.promise(key, GameAssetType.Image).then(asset => {
252
+ const asset_sampler = asset.create();
250
253
 
251
- return sampler2d_ensure_uint8_RGBA(asset_sampler);
252
- });
254
+ return sampler2d_ensure_uint8_RGBA(asset_sampler);
255
+ });
256
+ },
257
+ keyHashFunction: computeStringHash,
258
+ keyEqualityFunction: strictEquals
253
259
  });
254
260
 
255
261
  /**
@@ -1,50 +0,0 @@
1
- import { AbstractAsyncMap } from "./AbstractAsyncMap.js";
2
-
3
- /**
4
- * Cache wrapper that loads data when it's not found in the cache
5
- * @deprecated prefer to use {@link LoadingCache} instead
6
- */
7
- export class AsyncLoadingCache extends AbstractAsyncMap {
8
- /**
9
- *
10
- * @param {Map} cache
11
- * @param {function(K):Promise<T>} loader
12
- */
13
- constructor(cache, loader) {
14
- super();
15
-
16
- this.__cache = cache;
17
- this.__loader = loader;
18
- this.__pending = new Map();
19
- }
20
-
21
- async get(key) {
22
-
23
- const cached = this.__cache.get(key);
24
-
25
- if (cached !== undefined) {
26
- return cached;
27
- }
28
-
29
- let promise = this.__pending.get(key);
30
- let original_loader = false;
31
-
32
- if (promise === undefined) {
33
- promise = this.__loader(key);
34
-
35
- this.__pending.set(key, promise);
36
-
37
- original_loader = true;
38
- }
39
-
40
- const value = await promise;
41
-
42
- if (original_loader) {
43
- this.__cache.set(key, value);
44
- // remove from pending
45
- this.__pending.delete(key);
46
- }
47
-
48
- return value;
49
- }
50
- }
@@ -1,358 +0,0 @@
1
- import { assert } from "../../assert.js";
2
- import { BinaryBuffer } from "../../binary/BinaryBuffer.js";
3
- import { AbstractAsyncMap } from "./AbstractAsyncMap.js";
4
-
5
- /**
6
- * @template Key,Value
7
- */
8
- export class AsyncRemoteHashMap extends AbstractAsyncMap {
9
-
10
- /**
11
- *
12
- * @param keyHashFunction
13
- * @param keyEqualityFunction
14
- * @param {BinaryClassSerializationAdapter} keySerializationAdapter
15
- * @param {BinaryClassSerializationAdapter} valueSerializationAdapter
16
- * @param storageKeyPrefix
17
- * @param {Storage} storage
18
- */
19
- constructor({
20
- keyHashFunction,
21
- keyEqualityFunction,
22
- keySerializationAdapter,
23
- valueSerializationAdapter,
24
- storageKeyPrefix,
25
- storage
26
- }) {
27
- super();
28
-
29
- assert.isString(storageKeyPrefix, 'storageKeyPrefix');
30
-
31
- this._keyHashFunction = keyHashFunction;
32
- this._keyEqualityFunction = keyEqualityFunction;
33
- this._keySerializationAdapter = keySerializationAdapter;
34
- this._valueSerializationAdapter = valueSerializationAdapter;
35
- this._storageKeyPrefix = storageKeyPrefix;
36
- this._storage = storage;
37
-
38
- /**
39
- *
40
- * @type {Map<number,Promise>}
41
- * @private
42
- */
43
- this._bucket_locks = new Map();
44
- }
45
-
46
- /**
47
- *
48
- * @param {Storage} storage
49
- */
50
- attach_storage(storage) {
51
- this._storage = storage;
52
- }
53
-
54
- detach_storage() {
55
- this._storage = null;
56
- }
57
-
58
- async __get_bucket_lock(hash) {
59
- let source_lock = this._bucket_locks.get(hash);
60
- if (source_lock === undefined) {
61
- source_lock = Promise.resolve();
62
- }
63
-
64
- let is_resolved = false;
65
- const lock_object = {
66
- release() {
67
- is_resolved = true;
68
- }
69
- };
70
-
71
- const release_promise = new Promise((resolve, reject) => {
72
- lock_object.release = resolve;
73
-
74
- if (is_resolved) {
75
- resolve();
76
- }
77
- });
78
-
79
- const new_lock = source_lock.then(() => release_promise);
80
-
81
- this._bucket_locks.set(hash, new_lock);
82
-
83
- await source_lock;
84
-
85
- return lock_object;
86
- }
87
-
88
- /**
89
- * @param {number} hash
90
- * @return {Promise<ArrayBuffer|undefined>}
91
- * @private
92
- */
93
- __get_bucket_binary(hash) {
94
- return this._storage.promiseLoadBinary(this.__compute_bucket_storage_key(hash)).catch(() => {
95
- return undefined;
96
- });
97
- }
98
-
99
- __compute_bucket_storage_key(hash) {
100
- return `${this._storageKeyPrefix}/${hash}`;
101
- }
102
-
103
- /**
104
- *
105
- * @param {number} hash
106
- * @param {ArrayBuffer} bucket
107
- * @return {Promise}
108
- * @private
109
- */
110
- __set_bucket_binary(hash, bucket) {
111
- return this._storage.promiseStoreBinary(this.__compute_bucket_storage_key(hash), bucket);
112
- }
113
-
114
- /**
115
- *
116
- * @param {Key} key
117
- * @param {Value|undefined} value
118
- * @param {ArrayBuffer} buffer
119
- * @return {ArrayBuffer}
120
- * @private
121
- */
122
- __set_value_by_key_in_bucket(key, value, buffer) {
123
- const source = new BinaryBuffer();
124
- source.fromArrayBuffer(buffer);
125
-
126
- const record_count = buffer.byteLength >= 2 ? source.readUint16() : 0;
127
-
128
- let new_record_count = record_count;
129
-
130
- // first attempt to find the key
131
- const existing_value_address = this.__get_value_offset_from_bucket_by_key(key, buffer);
132
-
133
- if (existing_value_address === -1) {
134
- new_record_count++;
135
- }
136
-
137
- const destination = new BinaryBuffer();
138
- destination.writeUint16(new_record_count);
139
-
140
- let destination_data_offset = 2 + 16 * new_record_count;
141
- let write_cursor = 0;
142
- // copy old record over
143
- for (let i = 0; i < record_count; i++) {
144
- source.position = 2 + i * 16;
145
-
146
- const key_position = source.readUint32();
147
- const key_length = source.readUint32();
148
- const value_position = source.readUint32();
149
- const value_length = source.readUint32();
150
-
151
- if (existing_value_address !== -1 && value_position === existing_value_address) {
152
- // skip
153
- continue;
154
- }
155
-
156
- // write key + value
157
- destination.position = 2 + write_cursor * 16;
158
-
159
- // write header section
160
- destination.writeUint32(destination_data_offset);
161
- destination.writeUint32(key_length);
162
- destination.writeUint32(destination_data_offset + key_length);
163
- destination.writeUint32(value_length);
164
-
165
- destination.position = destination_data_offset;
166
- source.position = key_position;
167
- BinaryBuffer.copyBytes(source, destination, key_length);
168
-
169
- destination_data_offset += key_length;
170
-
171
- destination.position = destination_data_offset;
172
- source.position = value_position;
173
- BinaryBuffer.copyBytes(source, destination, value_length);
174
-
175
- destination_data_offset += value_length;
176
-
177
- write_cursor++;
178
- }
179
-
180
- const inserted_key_address = destination_data_offset;
181
- const inserted_key_length = this.__encode_object(destination, inserted_key_address, key, this._keySerializationAdapter);
182
-
183
- destination_data_offset += inserted_key_length;
184
- const inserted_value_address = destination_data_offset;
185
- const inserted_value_length = this.__encode_object(destination, inserted_value_address, value, this._valueSerializationAdapter);
186
-
187
-
188
- // write section for the new value
189
- destination.position = 2 + 16 * write_cursor;
190
- destination.writeUint32(inserted_key_address);
191
- destination.writeUint32(inserted_key_length);
192
- destination.writeUint32(inserted_value_address);
193
- destination.writeUint32(inserted_value_length);
194
-
195
- // restore position
196
- destination.position = inserted_value_address + inserted_value_length;
197
-
198
- destination.trim();
199
-
200
- return destination.data;
201
- }
202
-
203
- /**
204
- *
205
- * @param {Key} key
206
- * @param {ArrayBuffer} buffer
207
- * @return {number} -1 if not found, otherwise address within the buffer where the value can be read
208
- * @private
209
- */
210
- __get_value_offset_from_bucket_by_key(key, buffer) {
211
- const binary = new BinaryBuffer();
212
- binary.fromArrayBuffer(buffer);
213
-
214
- if (binary.length < 2) {
215
- return -1;
216
- }
217
-
218
- const record_count = binary.readUint16();
219
-
220
- if (record_count === 0) {
221
- return -1;
222
- }
223
-
224
- const key_offsets = new Uint32Array(record_count);
225
- const value_offsets = new Uint32Array(record_count);
226
-
227
- // read record offsets
228
- for (let i = 0; i < record_count; i++) {
229
- const key_offset = binary.readUint32();
230
- const key_length = binary.readUint32();
231
-
232
- const value_offset = binary.readUint32();
233
- const value_length = binary.readUint32();
234
-
235
- key_offsets[i] = key_offset;
236
- value_offsets[i] = value_offset;
237
- }
238
-
239
- // read key
240
- for (let i = 0; i < record_count; i++) {
241
- const key_address = key_offsets[i];
242
-
243
- const stored_key = this.__decode_object(binary, key_address, this._keySerializationAdapter);
244
-
245
- if (this._keyEqualityFunction(stored_key, key)) {
246
- return value_offsets[i]
247
- }
248
- }
249
-
250
- return -1;
251
- }
252
-
253
- /**
254
- *
255
- * @param {BinaryBuffer} buffer
256
- * @param {number} address
257
- * @param {BinaryClassSerializationAdapter} adapter
258
- * @return {Value}
259
- * @private
260
- */
261
- __decode_object(buffer, address, adapter) {
262
-
263
- buffer.position = address;
264
-
265
- const ValueKlass = adapter.getClass();
266
-
267
- const value = new ValueKlass();
268
-
269
- adapter.deserialize(buffer, value);
270
-
271
- return value;
272
- }
273
-
274
- /**
275
- * @template T
276
- * @param {BinaryBuffer} buffer
277
- * @param {number} address
278
- * @param {T} value
279
- * @param {BinaryClassSerializationAdapter} adapter
280
- * @returns {number} bytes written
281
- * @private
282
- */
283
- __encode_object(buffer, address, value, adapter) {
284
- buffer.position = address;
285
-
286
- adapter.serialize(buffer, value);
287
-
288
- const bytes_written = buffer.position - address;
289
-
290
- return bytes_written;
291
- }
292
-
293
- /**
294
- *
295
- * @param {BinaryBuffer} buffer
296
- * @param {number} address
297
- * @return {Value}
298
- * @private
299
- */
300
- __decode_value(buffer, address) {
301
- return this.__decode_object(buffer, address, this._valueSerializationAdapter);
302
- }
303
-
304
- /**
305
- *
306
- * @param {Key} key
307
- * @return {Promise<Value|undefined>}
308
- */
309
- async get(key) {
310
- // hash the key to identify bucket to load
311
- const key_hash = this._keyHashFunction(key);
312
-
313
- const lock = await this.__get_bucket_lock(key_hash);
314
-
315
- const bucket_buffer = await this.__get_bucket_binary(key_hash);
316
-
317
- lock.release();
318
-
319
- if (bucket_buffer === undefined) {
320
- return;
321
- }
322
-
323
- const value_address = this.__get_value_offset_from_bucket_by_key(key, bucket_buffer);
324
-
325
- if (value_address === -1) {
326
- return;
327
- }
328
-
329
- const binary = new BinaryBuffer();
330
- binary.fromArrayBuffer(bucket_buffer);
331
-
332
- return this.__decode_value(binary, value_address);
333
- }
334
-
335
- /**
336
- *
337
- * @param {Key} key
338
- * @param {Value} value
339
- * @return {Promise<void>}
340
- */
341
- async set(key, value) {
342
- const key_hash = this._keyHashFunction(key);
343
-
344
- const lock = await this.__get_bucket_lock(key_hash);
345
-
346
- let bucket_buffer = await this.__get_bucket_binary(key_hash);
347
-
348
- if (bucket_buffer === undefined) {
349
- bucket_buffer = new ArrayBuffer(0);
350
- }
351
-
352
- const updated_buffer = this.__set_value_by_key_in_bucket(key, value, bucket_buffer);
353
-
354
- await this.__set_bucket_binary(key_hash, updated_buffer);
355
-
356
- lock.release();
357
- }
358
- }
@@ -1,43 +0,0 @@
1
- import { AbstractAsyncMap } from "./AbstractAsyncMap.js";
2
-
3
- /**
4
- * Two-tier key-value storage. Intended to accelerate slow asynchronous
5
- * storage with a fast but limited (in space and reliability) cache in front.
6
- *
7
- * First tier (volatile) is considered to be fast an unreliable,
8
- * whereas second tier (persisted) is considered to be reliable, but slow.
9
- */
10
- export class CachedAsyncMap extends AbstractAsyncMap {
11
- /**
12
- *
13
- * @param {AbstractAsyncMap} volatile data here may be lost at any point, this is our "cache"
14
- * @param {AbstractAsyncMap} persisted data here is stored reliably
15
- */
16
- constructor(volatile, persisted) {
17
- super();
18
-
19
- this.__volatile = volatile;
20
- this.__persisted = persisted;
21
- }
22
-
23
- async get(key) {
24
- const volatile_value = await this.__volatile.get(key);
25
-
26
- if (volatile_value !== undefined && volatile_value !== null) {
27
- return volatile_value;
28
- }
29
-
30
- // TODO re-insert into volatile if persisted has a hit
31
-
32
- return await this.__persisted.get(key);
33
- }
34
-
35
- async set(key, value) {
36
- const lock = this.__volatile.set(key, value);
37
-
38
- // don't wait for completion on persisted
39
- this.__persisted.set(key, value);
40
-
41
- await lock;
42
- }
43
- }
@@ -1,47 +0,0 @@
1
- import { CachedAsyncMap } from "./CachedAsyncMap.js";
2
- import { AsyncMapWrapper } from "./AsyncMapWrapper.js";
3
-
4
- test("'get' retrieves data if it is available in volatile level", async () => {
5
-
6
- const first = new Map();
7
-
8
- const sut = new CachedAsyncMap(new AsyncMapWrapper(first), new AsyncMapWrapper());
9
-
10
- first.set(1, 3);
11
-
12
- expect(await sut.get(1)).toBe(3);
13
- });
14
-
15
- test("'get' retrieves data if it is available in persisted level", async () => {
16
-
17
- const second = new Map();
18
-
19
- const sut = new CachedAsyncMap(new AsyncMapWrapper(), new AsyncMapWrapper(second));
20
-
21
- second.set(1, 3);
22
-
23
- expect(await sut.get(1)).toBe(3);
24
- });
25
-
26
- test("'set' propagates data into volatile level", async () => {
27
-
28
- const first = new Map();
29
-
30
- const sut = new CachedAsyncMap(new AsyncMapWrapper(first), new AsyncMapWrapper());
31
-
32
- await sut.set(1, 3);
33
-
34
- expect(first.get(1)).toBe(3);
35
- });
36
-
37
-
38
- test("'set' propagates data into persisted level", async () => {
39
-
40
- const second = new Map();
41
-
42
- const sut = new CachedAsyncMap(new AsyncMapWrapper(), new AsyncMapWrapper(second));
43
-
44
- await sut.set(1, 3);
45
-
46
- expect(second.get(1)).toBe(3);
47
- });