minecraft-renderer 0.1.62 → 0.1.63

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecraft-renderer",
3
- "version": "0.1.62",
3
+ "version": "0.1.63",
4
4
  "description": "The most Modular Minecraft world renderer with Three.js WebGL backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -117,8 +117,8 @@ export const getDefaultRendererState = (): {
117
117
  return {
118
118
  reactive: proxy({
119
119
  world: {
120
- chunksLoaded: new Set<string>(),
121
- heightmaps: new Map<string, Int16Array>(),
120
+ chunksLoaded: {},
121
+ heightmaps: {},
122
122
  allChunksLoaded: false,
123
123
  mesherWork: false,
124
124
  instabilityFactors: defaultPerformanceInstabilityFactors(),
@@ -132,7 +132,7 @@ export const getDefaultRendererState = (): {
132
132
  worstRenderTime: 0,
133
133
  avgRenderTime: 0,
134
134
  world: {
135
- chunksLoaded: new Set(),
135
+ chunksLoadedCount: 0,
136
136
  chunksTotalNumber: 0,
137
137
  chunksFullInfo: '-'
138
138
  },
@@ -58,7 +58,7 @@ export interface NonReactiveState {
58
58
  worstRenderTime: number
59
59
  avgRenderTime: number
60
60
  world: {
61
- chunksLoaded: Set<string>
61
+ chunksLoadedCount: number
62
62
  chunksTotalNumber: number
63
63
  chunksFullInfo: string
64
64
  allChunksLoaded?: boolean
@@ -75,8 +75,8 @@ export interface NonReactiveState {
75
75
  /** Renderer reactive state */
76
76
  export interface RendererReactiveState {
77
77
  world: {
78
- chunksLoaded: Set<string>
79
- heightmaps: Map<string, Int16Array>
78
+ chunksLoaded: Record<string, true>
79
+ heightmaps: Record<string, Int16Array>
80
80
  allChunksLoaded: boolean
81
81
  mesherWork: boolean
82
82
  /** Low-FPS / render instability factors (see `performanceMonitor`). */
@@ -0,0 +1,65 @@
1
+ //@ts-nocheck
2
+ import { EventEmitter } from 'events'
3
+ import { describe, expect, it } from 'vitest'
4
+ import { bindAbortableEmitterListener, bindAbortableListener } from './bindAbortableListener'
5
+ import { WorldViewWorker } from '../worldView'
6
+
7
+ describe('bindAbortableListener', () => {
8
+ it('removes handler on abort', () => {
9
+ const emitter = new WorldViewWorker()
10
+ const controller = new AbortController()
11
+ let calls = 0
12
+ bindAbortableListener(emitter, 'renderDistance', () => {
13
+ calls++
14
+ }, controller.signal)
15
+
16
+ emitter.emit('renderDistance', 8)
17
+ expect(calls).toBe(1)
18
+
19
+ controller.abort()
20
+ emitter.emit('renderDistance', 12)
21
+ expect(calls).toBe(1)
22
+ })
23
+
24
+ it('abort removes only the bound handler on a shared emitter', () => {
25
+ const emitter = new WorldViewWorker()
26
+ const controllerA = new AbortController()
27
+ const controllerB = new AbortController()
28
+ let callsA = 0
29
+ let callsB = 0
30
+
31
+ bindAbortableListener(emitter, 'renderDistance', () => {
32
+ callsA++
33
+ }, controllerA.signal)
34
+ bindAbortableListener(emitter, 'renderDistance', () => {
35
+ callsB++
36
+ }, controllerB.signal)
37
+
38
+ emitter.emit('renderDistance', 4)
39
+ expect(callsA).toBe(1)
40
+ expect(callsB).toBe(1)
41
+
42
+ controllerA.abort()
43
+ emitter.emit('renderDistance', 6)
44
+ expect(callsA).toBe(1)
45
+ expect(callsB).toBe(2)
46
+ })
47
+ })
48
+
49
+ describe('bindAbortableEmitterListener', () => {
50
+ it('removes handler on abort', () => {
51
+ const emitter = new EventEmitter()
52
+ const controller = new AbortController()
53
+ let calls = 0
54
+ bindAbortableEmitterListener(emitter, 'test', () => {
55
+ calls++
56
+ }, controller.signal)
57
+
58
+ emitter.emit('test')
59
+ expect(calls).toBe(1)
60
+
61
+ controller.abort()
62
+ emitter.emit('test')
63
+ expect(calls).toBe(1)
64
+ })
65
+ })
@@ -0,0 +1,41 @@
1
+ //@ts-nocheck
2
+ import type { EventEmitter } from 'events'
3
+ import type { WorldViewEvents } from '../worldView/types'
4
+ import type { WorldViewWorker } from '../worldView'
5
+
6
+ /**
7
+ * Register an EventEmitter listener removed when `signal` aborts.
8
+ * Safe for shared emitters (e.g. worldView) — only removes this handler.
9
+ */
10
+ export function bindAbortableListener<E extends keyof WorldViewEvents>(
11
+ emitter: Pick<WorldViewWorker, 'on' | 'off'>,
12
+ event: E,
13
+ handler: (...args: WorldViewEvents[E]) => void,
14
+ signal: AbortSignal
15
+ ): void {
16
+ emitter.on(event, handler as (...args: any[]) => void)
17
+ if (signal.aborted) {
18
+ emitter.off(event, handler as (...args: any[]) => void)
19
+ return
20
+ }
21
+ signal.addEventListener('abort', () => {
22
+ emitter.off(event, handler as (...args: any[]) => void)
23
+ }, { once: true })
24
+ }
25
+
26
+ /** Same pattern for plain EventEmitters (e.g. resourcesManager). */
27
+ export function bindAbortableEmitterListener(
28
+ emitter: Pick<EventEmitter, 'on' | 'off'>,
29
+ event: string,
30
+ handler: (...args: any[]) => void,
31
+ signal: AbortSignal
32
+ ): void {
33
+ emitter.on(event, handler)
34
+ if (signal.aborted) {
35
+ emitter.off(event, handler)
36
+ return
37
+ }
38
+ signal.addEventListener('abort', () => {
39
+ emitter.off(event, handler)
40
+ }, { once: true })
41
+ }
@@ -86,104 +86,232 @@ export const useWorkerProxy = <T extends { __workerProxy: Record<string, (...arg
86
86
 
87
87
  const DEBUG_SYNC = false
88
88
 
89
- const sendWorkerSync = (syncId: string, obj: any, worker: Worker, debugKey: string) => {
90
- try {
91
- worker.postMessage({
92
- type: 'sync',
93
- syncId,
94
- value: cloneValtioObject(obj)
95
- })
96
- currentWorkerSyncStats.toWorker++
97
- globalThis.debugSyncMessagesOutgoing ??= 0
98
- globalThis.debugSyncMessagesOutgoing++
99
- } catch (err) {
100
- console.error('Failed to send worker sync', err)
101
- findProblemTransfer(obj)
102
- }
103
- }
89
+ // rendererState: worker→main only; playerState: main→worker only. Applying ops on the
90
+ // receiver re-fires local subscribers; no echo loop while directions stay split.
91
+
92
+ type SyncDirection = 'toWorker' | 'fromWorker'
93
+
94
+ export type WireSyncOp =
95
+ | { kind: 'set', path: (string | number | symbol)[], value: unknown }
96
+ | { kind: 'delete', path: (string | number | symbol)[] }
97
+
98
+ type ValtioOp = readonly unknown[]
104
99
 
105
- // Add stats tracking variables
106
100
  const currentWorkerSyncStats = { toWorker: 0, fromWorker: 0 }
107
101
 
108
- if (typeof window !== 'undefined') {
109
- setInterval(() => {
102
+ let debugSyncStatsInterval: ReturnType<typeof setInterval> | null = null
103
+
104
+ const ensureDebugSyncStatsInterval = () => {
105
+ if (debugSyncStatsInterval != null) return
106
+ if (typeof window === 'undefined') return
107
+ debugSyncStatsInterval = setInterval(() => {
110
108
  globalThis.debugWorkerSyncStats = { ...currentWorkerSyncStats }
111
109
  currentWorkerSyncStats.toWorker = 0
112
110
  currentWorkerSyncStats.fromWorker = 0
113
111
  }, 1000)
114
112
  }
115
113
 
114
+ const bumpSyncStat = (direction: SyncDirection) => {
115
+ ensureDebugSyncStatsInterval()
116
+ if (direction === 'toWorker') {
117
+ currentWorkerSyncStats.toWorker++
118
+ } else {
119
+ currentWorkerSyncStats.fromWorker++
120
+ }
121
+ }
122
+
123
+ /** @internal vitest only */
124
+ export const resetWorkerSyncStatsForTest = () => {
125
+ currentWorkerSyncStats.toWorker = 0
126
+ currentWorkerSyncStats.fromWorker = 0
127
+ if (debugSyncStatsInterval != null) {
128
+ clearInterval(debugSyncStatsInterval)
129
+ debugSyncStatsInterval = null
130
+ }
131
+ }
132
+
133
+ /** @internal vitest only */
134
+ export const getWorkerSyncStatsForTest = () => ({ ...currentWorkerSyncStats })
135
+
116
136
  const getSyncId = () => {
117
137
  return Math.random().toString(36).slice(2, 15) + Math.random().toString(36).slice(2, 15)
118
138
  }
119
139
 
120
- const applySyncPatch = (target: any, patch: any, worker: Worker) => {
121
- Object.assign(target, restoreTransferred(patch, [], worker, false))
140
+ export const setByPath = (target: any, path: (string | number | symbol)[], value: unknown) => {
141
+ if (path.length === 0) return
142
+ let cur = target
143
+ for (let i = 0; i < path.length - 1; i++) {
144
+ const key = path[i]!
145
+ if (cur[key] == null || typeof cur[key] !== 'object') {
146
+ cur[key] = {}
147
+ }
148
+ cur = cur[key]
149
+ }
150
+ cur[path[path.length - 1]!] = value
122
151
  }
123
152
 
124
- const setupObjectSync = (obj: any, originalObj: any, worker: Worker, isValtio: boolean, debugKey: string) => {
125
- if (!obj['__syncToWorker'] && !obj['__syncFromWorker'] && !isValtio) return
153
+ export const deleteByPath = (target: any, path: (string | number | symbol)[]) => {
154
+ if (path.length === 0) return
155
+ let cur = target
156
+ for (let i = 0; i < path.length - 1; i++) {
157
+ cur = cur[path[i]!]
158
+ if (cur == null) return
159
+ }
160
+ delete cur[path[path.length - 1]!]
161
+ }
126
162
 
127
- const syncId = getSyncId()
128
- obj['__syncId'] = syncId
163
+ export const prepareOpValueForTransfer = (value: any, worker: Worker): any => {
164
+ if (value == null || typeof value !== 'object') {
165
+ return value
166
+ }
129
167
 
130
- if (obj['__syncToWorker'] || isValtio) {
131
- const syncToWorker = () => {
132
- sendWorkerSync(syncId, originalObj, worker, `toWorker:${debugKey}`)
133
- }
134
- if (isValtio) {
135
- subscribe(originalObj, syncToWorker)
136
- }
168
+ if (value instanceof Vec3) {
169
+ return { x: value.x, y: value.y, z: value.z, __restorer: 'Vec3' }
170
+ }
137
171
 
138
- const interval = obj['__syncToWorkerInterval'] ?? 0
139
- if (interval > 0) {
140
- setInterval(syncToWorker, interval)
141
- }
172
+ if (typeof value['prepareForTransfer'] === 'function') {
173
+ return value['prepareForTransfer'](worker)
142
174
  }
143
175
 
144
- if (originalObj['__syncFromWorker']) {
145
- worker.addEventListener('message', (event: any) => {
146
- if (event.data.type === 'sync' && event.data.syncId === syncId) {
147
- currentWorkerSyncStats.fromWorker++
148
- applySyncPatch(originalObj, event.data.value, worker)
149
- }
150
- })
176
+ if (ArrayBuffer.isView(value)) {
177
+ return value
178
+ }
179
+
180
+ if (Array.isArray(value)) {
181
+ return value.map(item => prepareOpValueForTransfer(item, worker))
182
+ }
183
+
184
+ if (getVersion(value) !== undefined) {
185
+ return cloneValtioObject(value)
151
186
  }
187
+
188
+ const result = {} as any
189
+ for (const key in value) {
190
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
191
+ result[key] = prepareOpValueForTransfer(value[key], worker)
192
+ }
193
+ }
194
+ return result
152
195
  }
153
196
 
154
- const serializeMapForTransfer = (map: Map<unknown, unknown>) => ({
155
- __restorer: 'Map',
156
- __mapEntries: Array.from(map.entries()),
157
- })
197
+ const wireOpsFromValtioOps = (ops: ValtioOp[], worker: Worker): WireSyncOp[] => {
198
+ const wire: WireSyncOp[] = []
199
+ for (const op of ops) {
200
+ const kind = op[0]
201
+ if (kind === 'delete') {
202
+ wire.push({ kind: 'delete', path: op[1] as (string | number | symbol)[] })
203
+ } else if (kind === 'set') {
204
+ wire.push({
205
+ kind: 'set',
206
+ path: op[1] as (string | number | symbol)[],
207
+ value: prepareOpValueForTransfer(op[2], worker)
208
+ })
209
+ }
210
+ }
211
+ return wire
212
+ }
158
213
 
159
- const serializeSetForTransfer = (set: Set<unknown>) => ({
160
- __restorer: 'Set',
161
- __setValues: [...set],
162
- })
214
+ export const sendWorkerSyncOps = (
215
+ syncId: string,
216
+ ops: ValtioOp[],
217
+ worker: Worker,
218
+ direction: SyncDirection,
219
+ debugKey: string
220
+ ) => {
221
+ if (ops.length === 0) return
222
+ const wire = wireOpsFromValtioOps(ops, worker)
223
+ if (wire.length === 0) return
224
+ try {
225
+ worker.postMessage({ type: 'sync', syncId, ops: wire })
226
+ if (direction === 'toWorker') {
227
+ bumpSyncStat('toWorker')
228
+ }
229
+ if (DEBUG_SYNC) console.log(`sync ${debugKey}`, wire.length, 'ops')
230
+ } catch (err) {
231
+ console.error('Failed to send worker sync ops', err, debugKey)
232
+ for (const op of wire) {
233
+ if (op.kind === 'set') findProblemTransfer(op.value)
234
+ }
235
+ }
236
+ }
163
237
 
164
- const isSetLike = (value: unknown): value is Set<unknown> => {
165
- return value instanceof Set || Object.prototype.toString.call(value) === '[object Set]'
238
+ export const applySyncOps = (
239
+ target: any,
240
+ wireOps: WireSyncOp[],
241
+ worker: Worker,
242
+ countReceive: 'fromWorker' | false = false
243
+ ) => {
244
+ for (const op of wireOps) {
245
+ if (op.kind === 'delete') {
246
+ deleteByPath(target, op.path)
247
+ } else {
248
+ setByPath(target, op.path, restoreTransferred(op.value, [], worker, false, false))
249
+ }
250
+ }
251
+ if (countReceive === 'fromWorker') {
252
+ bumpSyncStat('fromWorker')
253
+ }
166
254
  }
167
255
 
168
- const isMapLike = (value: unknown): value is Map<unknown, unknown> => {
169
- return value instanceof Map || Object.prototype.toString.call(value) === '[object Map]'
256
+ /** Full snapshot for plain (non-Valtio) objects on interval sync only, e.g. nonReactiveState. */
257
+ const sendWorkerSyncSnapshot = (syncId: string, obj: any, worker: Worker, direction: SyncDirection, debugKey: string) => {
258
+ try {
259
+ const value = cloneValtioObject(obj)
260
+ worker.postMessage({ type: 'sync', syncId, value })
261
+ if (direction === 'toWorker') {
262
+ bumpSyncStat('toWorker')
263
+ }
264
+ if (DEBUG_SYNC) console.log(`sync snapshot ${debugKey}`)
265
+ } catch (err) {
266
+ console.error('Failed to send worker sync snapshot', err, debugKey)
267
+ findProblemTransfer(obj)
268
+ }
170
269
  }
171
270
 
172
- const iterableFromPlainObject = (obj: Record<string, unknown>) => {
173
- return Object.keys(obj)
174
- .filter(k => !k.startsWith('__'))
175
- .sort((a, b) => Number(a) - Number(b))
176
- .map(k => obj[k])
271
+ const applySyncSnapshot = (target: any, patch: any, worker: Worker, countReceive: 'fromWorker' | false = false) => {
272
+ Object.assign(target, restoreTransferred(patch, [], worker, false, false))
273
+ if (countReceive === 'fromWorker') {
274
+ bumpSyncStat('fromWorker')
275
+ }
177
276
  }
178
277
 
179
- const cloneValtioObject = (obj: any) => {
180
- if (isMapLike(obj)) {
181
- return serializeMapForTransfer(obj)
278
+ const setupObjectSync = (obj: any, originalObj: any, worker: Worker, isValtio: boolean, debugKey: string) => {
279
+ const syncFromWorker = obj['__syncFromWorker'] || originalObj['__syncFromWorker']
280
+ const syncToWorker = obj['__syncToWorker'] || originalObj['__syncToWorker']
281
+ if (!syncToWorker && !syncFromWorker && !isValtio) return
282
+
283
+ const syncId = getSyncId()
284
+ obj['__syncId'] = syncId
285
+
286
+ if (syncToWorker || isValtio) {
287
+ if (isValtio && syncToWorker !== false) {
288
+ subscribe(originalObj, (ops) => {
289
+ sendWorkerSyncOps(syncId, ops as ValtioOp[], worker, 'toWorker', `toWorker:${debugKey}`)
290
+ })
291
+ }
292
+
293
+ const interval = obj['__syncToWorkerInterval'] ?? originalObj['__syncToWorkerInterval'] ?? 0
294
+ if (interval > 0 && !isValtio) {
295
+ setInterval(() => {
296
+ sendWorkerSyncSnapshot(syncId, originalObj, worker, 'toWorker', `toWorker:interval:${debugKey}`)
297
+ }, interval)
298
+ }
182
299
  }
183
- if (isSetLike(obj)) {
184
- return serializeSetForTransfer(obj)
300
+
301
+ if (originalObj['__syncFromWorker']) {
302
+ worker.addEventListener('message', (event: any) => {
303
+ if (event.data.type === 'sync' && event.data.syncId === syncId) {
304
+ if (event.data.ops) {
305
+ applySyncOps(originalObj, event.data.ops, worker, 'fromWorker')
306
+ } else if (event.data.value) {
307
+ applySyncSnapshot(originalObj, event.data.value, worker, 'fromWorker')
308
+ }
309
+ }
310
+ })
185
311
  }
312
+ }
186
313
 
314
+ const cloneValtioObject = (obj: any) => {
187
315
  if (getVersion(obj) === undefined) {
188
316
  return obj
189
317
  }
@@ -215,21 +343,11 @@ export const deepPrepareForTransfer = (obj: any, worker: Worker, autoRemoveMetho
215
343
  continue
216
344
  }
217
345
 
218
- // print a warning for Date, RegExp, WeakMap, WeakSet
219
- if (obj[key] instanceof Date || obj[key] instanceof RegExp || obj[key] instanceof WeakMap || obj[key] instanceof WeakSet) {
346
+ // print a warning for Date, RegExp, Map, Set, WeakMap, WeakSet
347
+ if (obj[key] instanceof Date || obj[key] instanceof RegExp || obj[key] instanceof Map || obj[key] instanceof Set || obj[key] instanceof WeakMap || obj[key] instanceof WeakSet) {
220
348
  console.warn(`Warning: ${key} is a ${typeof obj[key]}, which is not supported for transfer.`)
221
349
  }
222
350
 
223
- // default restorers main -> worker
224
- if (isMapLike(obj[key])) {
225
- newObj[key] = serializeMapForTransfer(obj[key])
226
- continue
227
- }
228
- // Set (only primitive values)
229
- if (isSetLike(obj[key])) {
230
- newObj[key] = serializeSetForTransfer(obj[key])
231
- continue
232
- }
233
351
  if (obj[key] instanceof Vec3) {
234
352
  newObj[key] = { x: obj[key].x, y: obj[key].y, z: obj[key].z }
235
353
  newObj[key]['__restorer'] = 'Vec3'
@@ -247,6 +365,19 @@ export const deepPrepareForTransfer = (obj: any, worker: Worker, autoRemoveMetho
247
365
  const isValtio = getVersion(obj[key]) !== undefined
248
366
  newObj[key] = isValtio ? cloneValtioObject(obj[key]) : obj[key]
249
367
 
368
+ if (obj[key]['__syncFromWorker']) {
369
+ newObj[key]['__syncFromWorker'] = true
370
+ }
371
+ if (obj[key]['__syncToWorker']) {
372
+ newObj[key]['__syncToWorker'] = true
373
+ }
374
+ if (obj[key]['__syncFromWorkerInterval']) {
375
+ newObj[key]['__syncFromWorkerInterval'] = obj[key]['__syncFromWorkerInterval']
376
+ }
377
+ if (obj[key]['__syncToWorkerInterval']) {
378
+ newObj[key]['__syncToWorkerInterval'] = obj[key]['__syncToWorkerInterval']
379
+ }
380
+
250
381
  // Try to enable sync main -> worker
251
382
  const tryEnableDefaultSync = obj[key]['__syncToWorker'] !== false && !_isInsideValtio && isValtio && !obj[key]['__syncFromWorker']
252
383
  newObj[key]['__syncToWorker'] ??= tryEnableDefaultSync
@@ -258,6 +389,10 @@ export const deepPrepareForTransfer = (obj: any, worker: Worker, autoRemoveMetho
258
389
  setupObjectSync(newObj[key], originalObj[key], worker, true, key)
259
390
  continue
260
391
  }
392
+ if (newObj[key]['__syncFromWorker'] || newObj[key]['__syncToWorker']) {
393
+ setupObjectSync(newObj[key], originalObj[key], worker, isValtio, key)
394
+ continue
395
+ }
261
396
  setupObjectSync(newObj[key], originalObj[key], worker, false, key)
262
397
 
263
398
 
@@ -285,67 +420,52 @@ export const findProblemTransfer = (obj: any, path: string[] = []) => {
285
420
  }
286
421
  }
287
422
 
423
+ // Tracks which syncIds already have listeners/timers wired, per worker, so a
424
+ // given synced object is never armed twice (prevents runaway interval/listener
425
+ // accumulation if a payload carrying __sync* flags is ever restored again).
426
+ const armedSyncIds = new WeakMap<Worker, Set<string>>()
427
+
288
428
  const receiveSyncedObject = (obj: any, worker: Worker, debugKey: string) => {
289
429
  if (!obj['__syncId']) return
290
430
  const syncId = obj['__syncId']
291
431
 
432
+ let armed = armedSyncIds.get(worker)
433
+ if (!armed) {
434
+ armed = new Set()
435
+ armedSyncIds.set(worker, armed)
436
+ }
437
+ if (armed.has(syncId)) return
438
+ armed.add(syncId)
439
+
292
440
  if (obj['__syncToWorker']) {
293
441
  worker.addEventListener('message', (event: any) => {
294
442
  if (event.data.type === 'sync' && event.data.syncId === syncId) {
295
- applySyncPatch(obj, event.data.value, worker)
443
+ if (event.data.ops) {
444
+ applySyncOps(obj, event.data.ops, worker)
445
+ } else if (event.data.value) {
446
+ applySyncSnapshot(obj, event.data.value, worker)
447
+ }
296
448
  }
297
449
  })
298
450
  }
299
451
 
300
452
  if (obj['__syncFromWorker']) {
301
- const syncFromWorker = () => {
302
- sendWorkerSync(syncId, obj, worker, `fromWorker:${debugKey}`)
303
- }
304
-
305
453
  if (obj['__valtio']) {
306
- subscribe(obj, syncFromWorker)
454
+ subscribe(obj, (ops) => {
455
+ sendWorkerSyncOps(syncId, ops as ValtioOp[], worker, 'fromWorker', `fromWorker:${debugKey}`)
456
+ })
307
457
  }
308
458
 
309
459
  const interval = obj['__syncFromWorkerInterval'] ?? 0
310
- if (interval > 0) {
311
- setInterval(syncFromWorker, interval)
460
+ if (interval > 0 && !obj['__valtio']) {
461
+ setInterval(() => {
462
+ sendWorkerSyncSnapshot(syncId, obj, worker, 'fromWorker', `fromWorker:interval:${debugKey}`)
463
+ }, interval)
312
464
  }
313
465
  }
314
466
  }
315
467
 
316
468
  const defaultRestorers = [
317
- {
318
- restorerName: 'Map',
319
- restoreTransferred(obj, _worker: Worker) {
320
- if (Array.isArray(obj)) {
321
- return new Map(obj)
322
- }
323
- const raw = obj.__mapEntries ?? obj.entries
324
- if (Array.isArray(raw)) {
325
- return new Map(raw)
326
- }
327
- if (raw != null && typeof raw === 'object' && typeof raw !== 'function') {
328
- return new Map(Object.entries(raw as Record<string, unknown>))
329
- }
330
- return new Map()
331
- }
332
- },
333
- {
334
- restorerName: 'Set',
335
- restoreTransferred(obj, _worker: Worker) {
336
- if (Array.isArray(obj)) {
337
- return new Set(obj)
338
- }
339
- const raw = obj.__setValues ?? obj.values
340
- if (Array.isArray(raw)) {
341
- return new Set(raw)
342
- }
343
- if (raw != null && typeof raw === 'object' && typeof raw !== 'function') {
344
- return new Set(iterableFromPlainObject(raw as Record<string, unknown>))
345
- }
346
- return new Set()
347
- }
348
- },
349
469
  {
350
470
  restorerName: 'Vec3',
351
471
  restoreTransferred(obj, worker: Worker) {
@@ -358,7 +478,7 @@ export const addDefaultRestorer = (restorer: { restorerName: string, restoreTran
358
478
  defaultRestorers.unshift(restorer)
359
479
  }
360
480
 
361
- export const restoreTransferred = (obj: any, restorersArg: any[], worker: Worker, errorHandler: ((error: Error) => void) | boolean = true) => {
481
+ export const restoreTransferred = (obj: any, restorersArg: any[], worker: Worker, errorHandler: ((error: Error) => void) | boolean = true, armSync = true) => {
362
482
  const restorers = [...defaultRestorers, ...restorersArg]
363
483
 
364
484
  const restoreValue = (value: any, debugKey: string): any => {
@@ -400,7 +520,7 @@ export const restoreTransferred = (obj: any, restorersArg: any[], worker: Worker
400
520
  value = proxy(value)
401
521
  }
402
522
 
403
- receiveSyncedObject(value, worker, debugKey)
523
+ if (armSync) receiveSyncedObject(value, worker, debugKey)
404
524
  return value
405
525
  }
406
526