@vyr/remote 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/package.json +18 -0
  2. package/src/Bridge.ts +133 -0
  3. package/src/ChangeScriptable.ts +22 -0
  4. package/src/Collection.ts +36 -0
  5. package/src/RemoteExecutor.ts +514 -0
  6. package/src/RemoteInvoker.ts +72 -0
  7. package/src/RemoteProcess.ts +87 -0
  8. package/src/index.ts +7 -0
  9. package/src/job/IJob.ts +6 -0
  10. package/src/job/Job.ts +16 -0
  11. package/src/job/asset/add.ts +23 -0
  12. package/src/job/asset/dragdrap.ts +24 -0
  13. package/src/job/asset/index.ts +6 -0
  14. package/src/job/asset/load.ts +20 -0
  15. package/src/job/asset/remove.ts +20 -0
  16. package/src/job/asset/unload.ts +18 -0
  17. package/src/job/asset/update.ts +22 -0
  18. package/src/job/bridge/connection.ts +18 -0
  19. package/src/job/bridge/disconnect.ts +29 -0
  20. package/src/job/bridge/index.ts +2 -0
  21. package/src/job/index.ts +1 -0
  22. package/src/job/invoke/alert.ts +20 -0
  23. package/src/job/invoke/animation.ts +45 -0
  24. package/src/job/invoke/event.ts +40 -0
  25. package/src/job/invoke/index.ts +8 -0
  26. package/src/job/invoke/orbit.ts +20 -0
  27. package/src/job/invoke/pick.ts +15 -0
  28. package/src/job/invoke/properties.ts +43 -0
  29. package/src/job/invoke/screenshot.ts +35 -0
  30. package/src/job/invoke/transform.ts +36 -0
  31. package/src/job/scene/add.ts +22 -0
  32. package/src/job/scene/dragdrap.ts +23 -0
  33. package/src/job/scene/index.ts +6 -0
  34. package/src/job/scene/load.ts +19 -0
  35. package/src/job/scene/remove.ts +19 -0
  36. package/src/job/scene/unload.ts +17 -0
  37. package/src/job/scene/update.ts +20 -0
  38. package/src/locale/Language.ts +10 -0
  39. package/src/locale/LanguageProvider.ts +15 -0
  40. package/src/locale/index.ts +2 -0
  41. package/src/utils/index.ts +85 -0
  42. package/src/utils/screenshot.ts +20 -0
@@ -0,0 +1,514 @@
1
+ import { AnimationUnitInterpreter, Asset, Compilation, Descriptor, DeserializationObject, DivDescriptor, DynamicDescriptor, Engine, Graphics, HTMLDescriptor, HTMLServiceDescriptor, HTMLTransformControllerDescriptor, observer, PrefabeDescriptor, ServiceDescriptor, ServiceSchedulerDescriptor, StyleDescriptor, Unit, UpdateArgs } from '@vyr/engine'
2
+ import { cleanPickupObject, getAnimationUnit, createControllerStyle, pickupObject, getAnimationUnitByAsset, screenshot } from './utils'
3
+ import { Collection } from './Collection'
4
+ import { RemoteProcess } from './RemoteProcess'
5
+ import { Job } from './job'
6
+
7
+ class RemoteExecutor {
8
+ private _defaultScheduleUrl = Asset.createVirtualUrl('remote/schedule/default.ts')
9
+ readonly htmlTransformCollection = new Collection()
10
+ readonly process
11
+
12
+ constructor(process: RemoteProcess) {
13
+ this.process = process
14
+ }
15
+
16
+ async loadByScene(task: InstanceType<typeof Job['scene']['load']['Task']>) {
17
+ let loadTask = Asset.getTask(task.params.url)
18
+ if (loadTask === null) {
19
+ const nextTask = Asset.getTask(this.process.scene)
20
+ nextTask?.cancel()
21
+
22
+ this.process.scene = task.params.url
23
+ await Asset.loadAll(task.params.url)
24
+
25
+ //渲染新场景
26
+ const scheduler = Asset.get<ServiceSchedulerDescriptor>(task.params.url)
27
+ if (this.process.config.invoke) {
28
+ scheduler.traverse(sub => {
29
+ if (sub instanceof ServiceDescriptor) this.onAddService(sub)
30
+ })
31
+ }
32
+
33
+ this.sendAnimationUnitToRemote(0, getAnimationUnit(scheduler))
34
+
35
+ this.process.scheduler = scheduler
36
+ this.process.engine.switch(scheduler)
37
+ }
38
+ }
39
+ async unloadByScene(task: InstanceType<typeof Job['scene']['unload']['Task']>) {
40
+ const scheduler = Asset.get<ServiceSchedulerDescriptor>(this.process.scene)
41
+ if (scheduler !== null) {
42
+ scheduler.traverse(child => {
43
+ if (child instanceof ServiceDescriptor) this.onRemoveService(child)
44
+ })
45
+ }
46
+
47
+ const nextTask = Asset.getTask(this.process.scene)
48
+ nextTask?.cancel()
49
+ this.process.scene = ''
50
+ this.process.scheduler = new ServiceSchedulerDescriptor()
51
+ this.process.engine.switch(this.process.scheduler)
52
+ }
53
+ async addByScene(task: InstanceType<typeof Job['scene']['add']['Task']>) {
54
+ const target = Descriptor.get<Descriptor>(task.params.parent)
55
+ if (target === null) return
56
+
57
+ const subs: Descriptor[] = []
58
+ const tasks: Promise<void>[] = []
59
+ for (const node of task.params.node) {
60
+ const sub = Descriptor.create(node)
61
+ subs.push(sub)
62
+
63
+ const ports = Asset.graph.build(sub)
64
+ const dep = Asset.graph.getDependencidesByPorts(ports)
65
+ for (const url of dep.all) {
66
+ if (Asset.get(url) === null) tasks.push(Asset.loadAll(url, false))
67
+ }
68
+
69
+ if (this.process.config.invoke) {
70
+ sub.traverse(child => {
71
+ if (child instanceof ServiceDescriptor) this.onAddService(child)
72
+ })
73
+ }
74
+ }
75
+
76
+ await Promise.all(tasks)
77
+
78
+ if (task.params.next) {
79
+ for (const sub of subs) target.insertBefore(sub.uuid, task.params.next)
80
+ } else {
81
+ for (const sub of subs) target.add(sub)
82
+ }
83
+
84
+ await Asset.loadAll(this.process.scene)
85
+ this.sendAnimationUnitToRemote(0, getAnimationUnit(this.process.scheduler))
86
+ }
87
+ async removeyScene(task: InstanceType<typeof Job['scene']['remove']['Task']>) {
88
+ for (const uuid of task.params.uuids) {
89
+ const target = Descriptor.get<Descriptor>(uuid)
90
+ if (target === null) continue
91
+
92
+ this.resetTransformByRemoveJob(target)
93
+
94
+ const acestor = target.traceAncestor()
95
+ if (acestor.parent) acestor.parent.remove(target)
96
+
97
+ if (this.process.config.invoke) {
98
+ target.traverse(child => {
99
+ if (child instanceof ServiceDescriptor) this.onRemoveService(child)
100
+ })
101
+ }
102
+ }
103
+
104
+ await Asset.loadAll(this.process.scene)
105
+ this.sendAnimationUnitToRemote(0, getAnimationUnit(this.process.scheduler))
106
+ }
107
+ async dragdrapyScene(task: InstanceType<typeof Job['scene']['dragdrap']['Task']>) {
108
+ const root = Asset.get<Descriptor>(this.process.scene)
109
+ if (root) {
110
+
111
+ const method = task.params.next ? 'insertBefore' : task.params.type
112
+ const target = task.params.next || task.params.targetData.data.uuid
113
+
114
+ for (const uuid of task.params.dragData.data.uuids) {
115
+ root[method](uuid, target)
116
+ }
117
+ }
118
+ }
119
+ async updateyScene(task: InstanceType<typeof Job['scene']['update']['Task']>) {
120
+ const target = Descriptor.get<Descriptor>(task.params.content.uuid)
121
+ if (target === null) return
122
+
123
+ const temp = target.clone(false)
124
+ temp.syncWith(task.params.content)
125
+ const ports = Asset.graph.build(temp)
126
+ const dep = Asset.graph.getDependencidesByPorts(ports)
127
+ const tasks: Promise<void>[] = []
128
+ for (const url of dep.all) {
129
+ if (Asset.get(url) === null) tasks.push(Asset.loadAll(url, false))
130
+ }
131
+ await Promise.all(tasks)
132
+
133
+ this.updateOtherUpdateJob(target, task.params.content)
134
+ target.syncWith(task.params.content)
135
+ await Asset.loadAll(this.process.scene)
136
+
137
+ this.sendAnimationUnitToRemote(0, getAnimationUnit(this.process.scheduler))
138
+
139
+ target.setNeedsUpdate()
140
+ }
141
+
142
+ async loadByAsset(task: InstanceType<typeof Job['asset']['load']['Task']>) {
143
+ let loadTask = Asset.getTask(task.params.url)
144
+ if (loadTask === null) {
145
+ const nextTask = Asset.getTask(this.process.asset)
146
+ nextTask?.cancel()
147
+
148
+ this.process.asset = task.params.url
149
+ await Asset.loadAll(task.params.url)
150
+
151
+ const scheduler = new ServiceSchedulerDescriptor()
152
+ const descriptor = Asset.get<Descriptor>(task.params.url)
153
+ if (descriptor instanceof PrefabeDescriptor && descriptor.category === 'html') {
154
+ const service = new HTMLServiceDescriptor()
155
+ service.add(descriptor)
156
+ scheduler.add(service)
157
+ } else {
158
+ this.onLoadAsset(task.params.url, descriptor, scheduler)
159
+ }
160
+ scheduler.traverse(sub => {
161
+ if (sub instanceof ServiceDescriptor) this.onAddService(sub)
162
+ })
163
+
164
+ Asset.set(this._defaultScheduleUrl, scheduler)
165
+ Asset.graph.create(task.params.url)
166
+
167
+ this.sendAnimationUnitToRemote(0, getAnimationUnitByAsset(task.params.url))
168
+
169
+ this.process.scheduler = scheduler
170
+ this.process.engine.switch(scheduler)
171
+ }
172
+ }
173
+ async unloadByAsset(task: InstanceType<typeof Job['asset']['unload']['Task']>) {
174
+ const scheduler = Asset.get<ServiceSchedulerDescriptor>(this.process.asset)
175
+ if (scheduler !== null) {
176
+
177
+ if (this.process.config.invoke) {
178
+ scheduler.traverse(child => {
179
+ if (child instanceof ServiceDescriptor) this.onRemoveService(child)
180
+ })
181
+ }
182
+ }
183
+
184
+ const nextTask = Asset.getTask(this.process.asset)
185
+ nextTask?.cancel()
186
+ this.process.asset = ''
187
+
188
+ this.process.scheduler = new ServiceSchedulerDescriptor()
189
+ this.process.engine.switch(this.process.scheduler)
190
+ }
191
+ async addByAsset(task: InstanceType<typeof Job['asset']['add']['Task']>) {
192
+ const parent = Descriptor.get<Descriptor>(task.params.parent)
193
+ if (parent === null) return
194
+
195
+ const subs: Descriptor[] = []
196
+ const tasks: Promise<void>[] = []
197
+
198
+ for (const node of task.params.node) {
199
+ const sub = Descriptor.create(node)
200
+ subs.push(sub)
201
+
202
+ const ports = Asset.graph.build(sub)
203
+ const dep = Asset.graph.getDependencidesByPorts(ports)
204
+ for (const url of dep.all) {
205
+ if (Asset.get(url) === null) tasks.push(Asset.loadAll(url, false))
206
+ }
207
+
208
+ sub.traverse(child => {
209
+ if (child instanceof ServiceDescriptor) this.onAddService(child)
210
+ })
211
+ }
212
+ await Promise.all(tasks)
213
+
214
+ if (task.params.next) {
215
+ for (const sub of subs) parent.insertBefore(sub.uuid, task.params.next)
216
+ } else {
217
+ for (const sub of subs) parent.add(sub)
218
+ }
219
+
220
+ await Asset.loadAll(task.params.url)
221
+ Asset.graph.update(this._defaultScheduleUrl)
222
+
223
+ this.sendAnimationUnitToRemote(0, getAnimationUnitByAsset(task.params.url))
224
+ }
225
+ async removeyAsset(task: InstanceType<typeof Job['asset']['remove']['Task']>) {
226
+ for (const uuid of task.params.uuids) {
227
+ const target = Descriptor.get<Descriptor>(uuid)
228
+ if (target === null) continue
229
+
230
+ this.resetTransformByRemoveJob(target)
231
+
232
+ const acestor = target.traceAncestor()
233
+ if (acestor.parent) acestor.parent.remove(target)
234
+
235
+ if (this.process.config.invoke) {
236
+ target.traverse(child => {
237
+ if (child instanceof ServiceDescriptor) this.onRemoveService(child)
238
+ })
239
+ }
240
+ }
241
+
242
+ await Asset.loadAll(task.params.url)
243
+ this.sendAnimationUnitToRemote(0, getAnimationUnitByAsset(task.params.url))
244
+ }
245
+ async dragdrapyAsset(task: InstanceType<typeof Job['asset']['dragdrap']['Task']>) {
246
+ const root = Asset.get<Descriptor>(task.params.url)
247
+ if (root) {
248
+
249
+ const method = task.params.next ? 'insertBefore' : task.params.type
250
+ const target = task.params.next || task.params.targetData.data.uuid
251
+
252
+ for (const uuid of task.params.dragData.data.uuids) {
253
+ root[method](uuid, target)
254
+ }
255
+ }
256
+ }
257
+ async updateyAsset(task: InstanceType<typeof Job['asset']['update']['Task']>) {
258
+ const target = Descriptor.get<Descriptor>(task.params.content.uuid)
259
+ if (target === null) return
260
+
261
+ const temp = target.clone(false)
262
+ temp.syncWith(task.params.content)
263
+ const ports = Asset.graph.build(temp)
264
+ const dep = Asset.graph.getDependencidesByPorts(ports)
265
+ const tasks: Promise<void>[] = []
266
+ for (const url of dep.all) {
267
+ if (Asset.get(url) === null) tasks.push(Asset.loadAll(url, false))
268
+ }
269
+ await Promise.all(tasks)
270
+
271
+ this.updateOtherUpdateJob(target, task.params.content)
272
+ target.syncWith(task.params.content)
273
+ await Asset.loadAll(task.params.url)
274
+ Asset.graph.update(this._defaultScheduleUrl)
275
+
276
+ this.sendAnimationUnitToRemote(0, getAnimationUnitByAsset(task.params.url))
277
+
278
+ target.setNeedsUpdate()
279
+ }
280
+
281
+ clearHTMLTransform() {
282
+ const htmlTransforms = this.htmlTransformCollection.values()
283
+ for (const item of htmlTransforms) {
284
+ item.target = ''
285
+ item.mode = ''
286
+ item.enabled = false
287
+ item.setNeedsUpdate()
288
+ }
289
+ }
290
+ async transformByInvoke(task: InstanceType<typeof Job['invoke']['transform']['Task']>, service: ServiceDescriptor | null = null) {
291
+ this.clearHTMLTransform()
292
+
293
+ const target = Descriptor.get<Descriptor>(task.params.target)
294
+ if (service === null) {
295
+ service = ServiceDescriptor.traceService(target)
296
+ } else {
297
+ const isHTMLServiceDescriptor = service instanceof HTMLServiceDescriptor
298
+ if (isHTMLServiceDescriptor === false) return
299
+ }
300
+ if (service === null) return
301
+
302
+ const enabled = ['translate', 'rotate', 'scale'].includes(task.params.mode)
303
+ const htmlTransform = this.htmlTransformCollection.get<HTMLTransformControllerDescriptor>(service)
304
+
305
+ if (htmlTransform !== null && enabled === true && target instanceof HTMLDescriptor) {
306
+ htmlTransform.target = task.params.target
307
+ htmlTransform.mode = task.params.mode
308
+ htmlTransform.enabled = true
309
+ }
310
+ }
311
+ async orbitByInvoke(task: InstanceType<typeof Job['invoke']['orbit']['Task']>) { }
312
+ async propertiesByInvoke(task: InstanceType<typeof Job['invoke']['properties']['Task']>) { }
313
+ async animationByInvoke(task: InstanceType<typeof Job['invoke']['animation']['Task']>) {
314
+ if (task.reset) AnimationUnitInterpreter.reset(task.params.currentTime)
315
+ AnimationUnitInterpreter.enabled = task.params.enabled
316
+ }
317
+ async screenshot(task: InstanceType<typeof Job['invoke']['screenshot']['Task']>) {
318
+ const base64 = await screenshot(document.body)
319
+ const msg = new Job.invoke.screenshot.Response({ target: task.params.target, base64 })
320
+ this.process.bridge.send(msg)
321
+ }
322
+
323
+ onLoadAsset(url: string, descriptor: Descriptor, scheduler: ServiceSchedulerDescriptor) {
324
+ if (descriptor instanceof StyleDescriptor) {
325
+ const service = new HTMLServiceDescriptor()
326
+ service.add(new DivDescriptor({ style: url, text: '演示文字' }))
327
+ scheduler.add(service)
328
+ }
329
+ }
330
+ onAddService(service: ServiceDescriptor) {
331
+ if (service instanceof HTMLServiceDescriptor) {
332
+ let htmlTransform = this.htmlTransformCollection.get(service)
333
+ if (htmlTransform === null) {
334
+ htmlTransform = new HTMLTransformControllerDescriptor({ enabled: false, selectable: false })
335
+ this.htmlTransformCollection.set(service, htmlTransform)
336
+ htmlTransform.event = RemoteProcess.transformEvent
337
+ }
338
+ service.add(htmlTransform as HTMLTransformControllerDescriptor)
339
+ }
340
+ }
341
+ onRemoveService(service: ServiceDescriptor) {
342
+ if (service instanceof HTMLServiceDescriptor) {
343
+ const htmlTransform = this.htmlTransformCollection.get<HTMLTransformControllerDescriptor>(service)
344
+ if (htmlTransform !== null) {
345
+ this.htmlTransformCollection.delete(service)
346
+ service.remove(htmlTransform)
347
+ }
348
+ }
349
+ }
350
+ onUpdateScheduler(unit: Unit, args: UpdateArgs) {
351
+ const htmlTransform = Descriptor.get<Descriptor>(unit.uuid)
352
+ const service = this.htmlTransformCollection.getByMap(htmlTransform)
353
+ if (service !== null) {
354
+ //@ts-ignore
355
+ window['__VYR_RUNTIME.DISABLED_INPUT'] = false
356
+ }
357
+ }
358
+
359
+ resetTransformByRemoveJob(target: Descriptor, service: ServiceDescriptor | null = null) {
360
+ if (service === null) service = ServiceDescriptor.traceService(target)
361
+
362
+ if (service instanceof HTMLServiceDescriptor) {
363
+ const htmlTransform = this.htmlTransformCollection.get<HTMLTransformControllerDescriptor>(service)
364
+ if (htmlTransform === null) return
365
+
366
+ if (htmlTransform.target === target.uuid) {
367
+ htmlTransform.target = ''
368
+ htmlTransform.enabled = false
369
+ htmlTransform.setNeedsUpdate()
370
+ }
371
+ }
372
+ }
373
+ private _updateStyle(target: StyleDescriptor, deserialization: DeserializationObject<StyleDescriptor>) {
374
+ if (target.inherit === deserialization.inherit) return
375
+ observer.trigger('updateDeps', { self: target.uuid })
376
+ }
377
+ updateOtherUpdateJob(target: Descriptor, deserialization: DeserializationObject<Descriptor>, service: ServiceDescriptor | null = null) {
378
+ if (target instanceof StyleDescriptor) this._updateStyle(target, deserialization as any)
379
+ if (service === null) {
380
+ service = ServiceDescriptor.traceService(target)
381
+ }
382
+ if (service === null) return
383
+ const htmlTransform = this.htmlTransformCollection.get<HTMLTransformControllerDescriptor>(service)
384
+ if (htmlTransform === null) return
385
+ if (htmlTransform.target === target.uuid) {
386
+ this.process.engine.listen('afterRender', () => htmlTransform.setNeedsUpdate(), { once: true })
387
+ }
388
+ }
389
+
390
+ enhance(process: RemoteProcess, schedulerWrapper: ((scheduler: ServiceSchedulerDescriptor) => void) | null = null) {
391
+ const executor = process.getExecutor()
392
+ const listen = Compilation.prototype.listen
393
+
394
+ Compilation.prototype.listen = function (scheduler: ServiceSchedulerDescriptor, engine: Engine) {
395
+ if (schedulerWrapper !== null) schedulerWrapper(scheduler)
396
+ listen.call(this, scheduler, engine)
397
+ const graphics = engine.getGraphics(scheduler)
398
+ if (graphics === null || graphics.__VYR_REMOTE_WRAPPER === true) return graphics
399
+ graphics.__VYR_REMOTE_WRAPPER = true
400
+
401
+ return graphics
402
+ }
403
+
404
+ const doUpdate = Graphics.prototype.doUpdate
405
+ Graphics.prototype.doUpdate = function (unit: Unit, args: UpdateArgs) {
406
+ if (this.__VYR_REMOTE_WRAPPER === true) {
407
+ //@ts-ignore
408
+ window['__VYR_RUNTIME.DISABLED_INPUT'] = true
409
+ executor.onUpdateScheduler(unit, args)
410
+ doUpdate.call(this, unit, args)
411
+ } else {
412
+ doUpdate.call(this, unit, args)
413
+ }
414
+ }
415
+
416
+ process.engine.listen('afterRender', (args) => this.sendAnimationUnitToRemote(args.delta))
417
+ }
418
+
419
+ protected stopWheel(e: WheelEvent) {
420
+ if (e.ctrlKey === true) {
421
+ e.preventDefault()
422
+ e.stopPropagation()
423
+ }
424
+ }
425
+ protected onEvent(event: MouseEvent | KeyboardEvent) {
426
+ let task
427
+ if (event instanceof MouseEvent) {
428
+ task = new Job.invoke.event.Response({
429
+ type: 'mouse',
430
+ properties: {
431
+ type: event.type,
432
+ bubbles: event.bubbles,
433
+ cancelable: event.cancelable,
434
+ altKey: event.altKey,
435
+ ctrlKey: event.ctrlKey,
436
+ shiftKey: event.shiftKey,
437
+ clientX: event.clientX,
438
+ clientY: event.clientY,
439
+ }
440
+ })
441
+ } else {
442
+ task = new Job.invoke.event.Response({
443
+ type: 'keyboard',
444
+ properties: {
445
+ type: event.type,
446
+ altKey: event.altKey,
447
+ ctrlKey: event.ctrlKey,
448
+ shiftKey: event.shiftKey,
449
+ code: event.code,
450
+ }
451
+ })
452
+ if (event.code === 'Tab') event.preventDefault()
453
+ }
454
+ this.process.bridge.send(task)
455
+ }
456
+ protected onMessage(args: any) {
457
+ if (args.type === DynamicDescriptor.type) {
458
+ //动态节点发生了循环引用,动态节点 arsg.uuid
459
+ }
460
+ }
461
+
462
+ sendAnimationUnitToRemote(delta: number, max?: number) {
463
+ this.process.bridge.send(new Job.invoke.animation.Response({ type: 'unit', delta, max }))
464
+ }
465
+
466
+ async listen() {
467
+ window.addEventListener('wheel', this.stopWheel, { passive: false })
468
+
469
+ if (this.process.config.execute) {
470
+ this.process.bridge.listen(Job.asset.load.Method, this.loadByAsset.bind(this))
471
+ this.process.bridge.listen(Job.asset.unload.Method, this.unloadByAsset.bind(this))
472
+ this.process.bridge.listen(Job.asset.add.Method, this.addByAsset.bind(this))
473
+ this.process.bridge.listen(Job.asset.remove.Method, this.removeyAsset.bind(this))
474
+ this.process.bridge.listen(Job.asset.dragdrap.Method, this.dragdrapyAsset.bind(this))
475
+ this.process.bridge.listen(Job.asset.update.Method, this.updateyAsset.bind(this))
476
+
477
+ this.process.bridge.listen(Job.scene.load.Method, this.loadByScene.bind(this))
478
+ this.process.bridge.listen(Job.scene.unload.Method, this.unloadByScene.bind(this))
479
+ this.process.bridge.listen(Job.scene.add.Method, this.addByScene.bind(this))
480
+ this.process.bridge.listen(Job.scene.remove.Method, this.removeyScene.bind(this))
481
+ this.process.bridge.listen(Job.scene.dragdrap.Method, this.dragdrapyScene.bind(this))
482
+ this.process.bridge.listen(Job.scene.update.Method, this.updateyScene.bind(this))
483
+ }
484
+
485
+ if (this.process.config.invoke) {
486
+ createControllerStyle()
487
+ pickupObject(this.process)
488
+ cleanPickupObject(this.process)
489
+ this.enhance(this.process)
490
+
491
+ this.process.bridge.listen(Job.invoke.transform.Method, this.transformByInvoke.bind(this))
492
+ this.process.bridge.listen(Job.invoke.orbit.Method, this.orbitByInvoke.bind(this))
493
+ this.process.bridge.listen(Job.invoke.properties.Method, this.propertiesByInvoke.bind(this))
494
+ this.process.bridge.listen(Job.invoke.animation.Method, this.animationByInvoke.bind(this))
495
+ this.process.bridge.listen(Job.invoke.screenshot.Method, this.screenshot.bind(this))
496
+
497
+ window.addEventListener('keydown', this.onEvent.bind(this))
498
+ window.addEventListener('keyup', this.onEvent.bind(this))
499
+ window.addEventListener('mousedown', this.onEvent.bind(this))
500
+ window.addEventListener('click', this.onEvent.bind(this))
501
+ window.addEventListener('mouseup', this.onEvent.bind(this))
502
+
503
+ //@ts-ignore
504
+ window['__VYR_RUNTIME.DISABLED_INPUT'] = true
505
+ }
506
+
507
+ observer.listen('__VYR_RUNTIME@remote', this.onMessage.bind(this))
508
+ }
509
+ }
510
+
511
+
512
+ export {
513
+ RemoteExecutor
514
+ }
@@ -0,0 +1,72 @@
1
+ import { AsyncTask, Generate } from '@vyr/engine'
2
+ import { Bridge, RemoteConfig } from './Bridge'
3
+
4
+ interface RemoteSanbox {
5
+ [k: string]: any
6
+ DOM: HTMLDivElement
7
+ useRemote: () => Promise<Window>
8
+ }
9
+
10
+ /**远程调用器 */
11
+ class RemoteInvoker {
12
+ static createRemoteLocation(config: RemoteConfig) {
13
+ const location = Bridge.createBaseUrl() + `/preview.html?config=${JSON.stringify(config)}`
14
+ return location
15
+ }
16
+ static createIframe(location: string) {
17
+ const DOM = document.createElement('iframe')
18
+ DOM.width = '100%'
19
+ DOM.height = '100%'
20
+ DOM.style.width = '100%'
21
+ DOM.style.height = '100%'
22
+ DOM.style.border = 'none'
23
+ DOM.setAttribute('src', location)
24
+ const promise = new Promise<Window>((resolve) => DOM.addEventListener('load', () => resolve(DOM.contentWindow as Window)))
25
+ const task = new AsyncTask(() => promise)
26
+ const listeners = ['keydown', 'keyup', 'mousedown', 'mouseup']
27
+ const stop = (e: Event) => { e.preventDefault(); e.stopPropagation() }
28
+ for (const key of listeners) DOM.addEventListener(key, stop)
29
+ const useRemote = async () => task.done()
30
+ const snabox: RemoteSanbox = { DOM, useRemote }
31
+ task.run()
32
+ return snabox
33
+ }
34
+ static createWindow(location: string) {
35
+ const DOM = document.createElement('div')
36
+ const win = window.open(location)
37
+ const snabox: RemoteSanbox = { DOM, useRemote: async () => win as Window }
38
+ return snabox
39
+ }
40
+ static createSandbox(config: RemoteConfig) {
41
+ const location = this.createRemoteLocation(config)
42
+ return config.env === 'window' ? this.createWindow(location) : this.createIframe(location)
43
+ }
44
+
45
+ private sanbox
46
+ readonly id
47
+ readonly bridge
48
+
49
+ constructor(window: Window, options: Partial<Omit<RemoteConfig, 'id'>> = {}) {
50
+ const config: RemoteConfig = { id: `${Generate.id()}`, env: 'window', execute: false, invoke: false }
51
+ Object.assign(config, options)
52
+ this.id = config.id
53
+ this.sanbox = RemoteInvoker.createSandbox(config)
54
+ this.bridge = new Bridge({ id: config.id, useRemote: this.sanbox.useRemote })
55
+ }
56
+
57
+ mount(id: string) {
58
+ if (this.sanbox === null) return
59
+ const container = document.getElementById(id)
60
+ if (container !== null) container.appendChild(this.sanbox.DOM)
61
+ }
62
+
63
+ dispose() {
64
+ this.bridge.close()
65
+ }
66
+
67
+ async ready() {
68
+ return this.bridge.connected.done()
69
+ }
70
+ }
71
+
72
+ export { RemoteInvoker }
@@ -0,0 +1,87 @@
1
+ import { Asset, Descriptor, Engine, Generate, ServiceDescriptor, ServiceSchedulerDescriptor } from '@vyr/engine'
2
+ import { Bridge, RemoteConfig } from './Bridge'
3
+ import { RemoteExecutor } from './RemoteExecutor'
4
+ import { Job } from './job'
5
+
6
+ type RemoteSetup = (process: RemoteProcess) => void
7
+
8
+ const privateState = {
9
+ setupCollection: [] as RemoteSetup[],
10
+ executorCollection: new Map<string, RemoteProcess>(),
11
+ }
12
+
13
+ class RemoteProcess {
14
+ static transformEvent = `TransformController/event/${Generate.uuid()}.ts`
15
+ static get(engine: Engine) {
16
+ return privateState.executorCollection.get(engine.uuid) ?? null
17
+ }
18
+ static register(setup: RemoteSetup) {
19
+ privateState.setupCollection.push(setup)
20
+ }
21
+ static getServices(descriptor: Descriptor, services: Descriptor[] = []) {
22
+ descriptor.traverse(sub => {
23
+ if (sub instanceof ServiceDescriptor) services.push(sub)
24
+ })
25
+ return services
26
+ }
27
+
28
+ private _executor!: RemoteExecutor
29
+ readonly config
30
+ readonly element
31
+ readonly engine
32
+ scene = ''
33
+ asset = ''
34
+ scheduler: ServiceSchedulerDescriptor
35
+ bridge!: Bridge
36
+
37
+ constructor(element: string | HTMLElement, engine = new Engine()) {
38
+ privateState.executorCollection.set(engine.uuid, this)
39
+ this.element = element
40
+ this.engine = engine
41
+ this.config = this._initConfig()
42
+ this.scheduler = new ServiceSchedulerDescriptor()
43
+ }
44
+
45
+ private _initConfig() {
46
+ const params: { [k: string]: string } = {}
47
+ const url = document.location.search
48
+ const kvdata = url.slice(1).split('&')
49
+ for (const kv of kvdata) {
50
+ const kvClips = kv.split('=')
51
+ params[kvClips[0]] = kvClips[1]
52
+ }
53
+ return (params.config ? JSON.parse(decodeURIComponent(params.config)) : {}) as RemoteConfig
54
+ }
55
+
56
+ async listen() {
57
+ for (const setup of privateState.setupCollection) setup(this)
58
+
59
+ this.bridge = new Bridge({ id: this.config.id, args: [this] })
60
+
61
+ let executor = this.getExecutor()
62
+ if (executor === null) {
63
+ executor = new RemoteExecutor(this)
64
+ this.setExecutor(executor)
65
+ }
66
+
67
+ await executor.listen()
68
+
69
+ this.engine.run(this.element)
70
+
71
+ if (this.config.scene) executor.loadByScene(new Job.scene.load.Task({ url: this.config.scene }))
72
+ }
73
+
74
+ setExecutor(executor: RemoteExecutor) {
75
+ this._executor = executor
76
+ }
77
+ getExecutor<T extends RemoteExecutor = RemoteExecutor>() {
78
+ return this._executor as T
79
+ }
80
+ }
81
+
82
+ Asset.provider(RemoteProcess.transformEvent, () => import('./ChangeScriptable'))
83
+ Asset.load(RemoteProcess.transformEvent)
84
+
85
+ export {
86
+ RemoteProcess
87
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './locale'
2
+ export * from './job'
3
+ export * from './Bridge'
4
+ export * from './Collection'
5
+ export * from './RemoteExecutor'
6
+ export * from './RemoteProcess'
7
+ export * from './RemoteInvoker'
@@ -0,0 +1,6 @@
1
+ abstract class IJob {
2
+ abstract readonly method: string
3
+ abstract params: { [k: string]: any }
4
+ }
5
+
6
+ export { IJob }