Skip to content

Commit f01a092

Browse files
wip
1 parent e6516b3 commit f01a092

File tree

3 files changed

+261
-220
lines changed

3 files changed

+261
-220
lines changed

src/components/EngineStream.tsx

Lines changed: 75 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const EngineStream = (props: {
4949

5050
// These will be passed to the engineStreamActor to handle.
5151
const videoRef = useRef<HTMLVideoElement>(null)
52-
const canvasRef = useRef<HTMLVideoElement>(null)
52+
const canvasRef = useRef<HTMLCanvasElement>(null)
5353

5454
// For attaching right-click menu events
5555
const videoWrapperRef = useRef<HTMLDivElement>(null)
@@ -68,10 +68,31 @@ export const EngineStream = (props: {
6868
const streamIdleMode = settings.app.streamIdleMode.current
6969

7070
useEffect(() => {
71-
engineStreamActor.send({ type: SetVideoRef, videoRef })
72-
engineStreamActor.send({ type: SetCanvasRef, canvasRef })
73-
}, [videoRef.current, canvasRef.current])
71+
engineStreamActor.send({ type: EngineStreamTransition.SetVideoRef, videoRef })
72+
}, [videoRef.current])
7473

74+
useEffect(() => {
75+
engineStreamActor.send({ type: EngineStreamTransition.SetCanvasRef, canvasRef })
76+
}, [canvasRef.current])
77+
78+
useEffect(() => {
79+
engineStreamActor.send({
80+
type: EngineStreamTransition.SetPool,
81+
pool: props.pool,
82+
})
83+
}, [props.pool])
84+
85+
useEffect(() => {
86+
engineStreamActor.send({
87+
type: EngineStreamTransition.SetAuthToken,
88+
authToken: props.authToken,
89+
})
90+
}, [props.authToken])
91+
92+
// We have to call this here because of the dependencies:
93+
// modelingMachineActorSend, setAppState, settingsEngine
94+
// It's possible to pass these in earlier but I (lee) don't want to
95+
// restructure this further at the moment.
7596
const startOrReconfigureEngine = () => {
7697
engineStreamActor.send({
7798
type: EngineStreamTransition.StartOrReconfigureEngine,
@@ -87,12 +108,39 @@ export const EngineStream = (props: {
87108
})
88109
}
89110

90-
// When the scene is ready play the stream and execute!
111+
useEffect(() => {
112+
if (engineStreamState.value !== EngineStreamState.Configuring) return
113+
startOrReconfigureEngine()
114+
}, [engineStreamState, setAppState])
115+
116+
// ...Object.values(settingsEngine),
117+
118+
// I would inline this but it needs to be a function for removeEventListener.
91119
const play = () => {
92120
engineStreamActor.send({
93121
type: EngineStreamTransition.Play,
94122
})
123+
}
95124

125+
useEffect(() => {
126+
engineCommandManager.addEventListener(
127+
EngineCommandManagerEvents.SceneReady,
128+
play
129+
)
130+
return () => {
131+
engineCommandManager.removeEventListener(
132+
EngineCommandManagerEvents.SceneReady,
133+
play
134+
)
135+
}
136+
}, [])
137+
138+
// When the scene is ready play the stream and execute!
139+
const executeKcl = () => {
140+
// It's ok to position the camera before modeling commands
141+
sceneInfra.camControls.restoreRemoteCameraStateAndTriggerSync().catch(trap)
142+
143+
console.log('scene is ready, execute kcl')
96144
const kmp = kclManager.executeCode().catch(trap)
97145

98146
if (!firstPlay) return
@@ -101,7 +149,7 @@ export const EngineStream = (props: {
101149
// Reset the restart timeouts
102150
setAttemptTimes([0, 1000])
103151

104-
console.log('scene is ready, execute kcl')
152+
console.log('firstPlay true, zoom to fit')
105153

106154
kmp
107155
.then(() =>
@@ -125,91 +173,39 @@ export const EngineStream = (props: {
125173
useEffect(() => {
126174
engineCommandManager.addEventListener(
127175
EngineCommandManagerEvents.SceneReady,
128-
play
176+
executeKcl
129177
)
130178
return () => {
131-
engineCommandManager.removeEventListener(
132-
EngineCommandManagerEvents.SceneReady,
133-
play
134-
)
179+
engineStreamActor.send({ type: EngineStreamTransition.Pause })
135180
}
136-
}, [firstPlay])
137-
138-
// We do a back-off restart, using a fibonacci sequence, since it
139-
// has a nice retry time curve (somewhat quick then exponential)
140-
const restart = () => {
141-
setTimeout(() => {
142-
setFirstPlay(false)
143-
setAttemptTimes([attemptTimes[1], attemptTimes[0] + attemptTimes[1]])
144-
engineCommandManager.tearDown()
145-
startOrReconfigureEngine()
146-
}, attemptTimes[0] + attemptTimes[1])
147-
}
181+
}, [])
148182

149183
useEffect(() => {
150-
engineCommandManager.addEventListener(
151-
EngineCommandManagerEvents.SceneReady,
152-
play
153-
)
184+
// We do a back-off restart, using a fibonacci sequence, since it
185+
// has a nice retry time curve (somewhat quick then exponential)
186+
const restart = () => {
187+
setTimeout(() => {
188+
setFirstPlay(false)
189+
setAttemptTimes([attemptTimes[1], attemptTimes[0] + attemptTimes[1]])
190+
engineStreamState.context.videoRef.current.pause()
191+
engineCommandManager.tearDown()
192+
startOrReconfigureEngine()
193+
}, attemptTimes[0] + attemptTimes[1])
194+
}
195+
154196
engineCommandManager.addEventListener(
155197
EngineCommandManagerEvents.EngineRestartRequest,
156198
restart
157199
)
158-
engineStreamActor.send({
159-
type: EngineStreamTransition.SetPool,
160-
data: { pool: props.pool },
161-
})
162-
engineStreamActor.send({
163-
type: EngineStreamTransition.SetAuthToken,
164-
data: { authToken: props.authToken },
165-
})
166-
167200
return () => {
168-
engineStreamActor.send({ type: EngineStreamTransition.Pause })
169-
}
170-
}, [])
171-
172-
// In the past we'd try to play immediately, but the proper thing is to way
173-
// for the 'canplay' event to tell us data is ready.
174-
useEffect(() => {
175-
const videoRef = engineStreamState.context.videoRef.current
176-
if (!videoRef) {
177-
return
178-
}
179-
180-
const onCanPlay = () => {
181-
videoRef.play().catch(console.error)
182-
}
183-
184-
// I (lee) believe that the engine handles serving video first better
185-
// than receiving commands when it's expected to. Only when the
186-
// browser reports we're receiving video do we trigger KCL execution.
187-
const onPlay = () => {
188-
if (!engineCommandManager.engineConnection) { return }
189-
190-
engineCommandManager.engineConnection.state = {
191-
type: EngineConnectionStateType.ConnectionEstablished,
192-
}
193-
194-
engineCommandManager.inSequence = 1
195-
196-
engineCommandManager.engineConnection.dispatchEvent(
197-
new CustomEvent(EngineConnectionEvents.Opened, {
198-
detail: engineCommandManager.engineConnection,
199-
})
201+
engineCommandManager.removeEventListener(
202+
EngineCommandManagerEvents.EngineRestartRequest,
203+
restart
200204
)
201-
markOnce('code/endInitialEngineConnect')
202205
}
206+
}, [engineStreamState, attemptTimes])
203207

204-
videoRef.addEventListener('canplay', onCanPlay)
205-
videoRef.addEventListener('play', onPlay)
206-
return () => {
207-
videoRef.removeEventListener('canplay', onCanPlay)
208-
videoRef.removeEventListener('play', onPlay)
209-
}
210-
}, [engineStreamState.context.videoRef.current])
211-
212-
useEffect(() => {
208+
useEffect(() => {
213209
if (engineStreamState.value === EngineStreamState.Reconfiguring) return
214210
const video = engineStreamState.context.videoRef?.current
215211
if (!video) return
@@ -235,12 +231,6 @@ export const EngineStream = (props: {
235231
}).observe(document.body)
236232
}, [engineStreamState.value])
237233

238-
// On settings change, reconfigure the engine. When paused this gets really tricky,
239-
// and also requires onMediaStream to be set!
240-
useEffect(() => {
241-
startOrReconfigureEngine()
242-
}, Object.values(settingsEngine))
243-
244234
/**
245235
* Subscribe to execute code when the file changes
246236
* but only if the scene is already ready.
@@ -316,18 +306,7 @@ export const EngineStream = (props: {
316306
}
317307

318308
if (engineStreamState.value === EngineStreamState.Paused) {
319-
engineStreamActor.send({
320-
type: EngineStreamTransition.StartOrReconfigureEngine,
321-
modelingMachineActorSend,
322-
settings: settingsEngine,
323-
setAppState,
324-
onMediaStream(mediaStream: MediaStream) {
325-
engineStreamActor.send({
326-
type: EngineStreamTransition.SetMediaStream,
327-
mediaStream,
328-
})
329-
},
330-
})
309+
startOrReconfigureEngine()
331310
}
332311

333312
timeoutStart.current = Date.now()

src/lang/std/engineConnection.ts

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,12 @@ class EngineConnection extends EventTarget {
714714
this.disconnectAll()
715715
break
716716

717+
// The remote end broke up with us! :(
717718
case 'disconnected':
719+
this.engineConnection?.dispatchEvent(
720+
new CustomEvent(EngineConnectionEvents.RestartRequest, {})
721+
)
722+
break
718723
case 'closed':
719724
this.pc?.removeEventListener('icecandidate', this.onIceCandidate)
720725
this.pc?.removeEventListener('icegatheringstatechange', this.onIceGatheringStateChange)
@@ -824,6 +829,25 @@ class EngineConnection extends EventTarget {
824829
type: ConnectingType.DataChannelEstablished,
825830
},
826831
}
832+
833+
// Start firing off engine commands at this point.
834+
// They could be fired at an earlier time, onWebSocketOpen,
835+
// but DataChannel can offer some benefits like speed,
836+
// and it's nice to say everything's connected before interacting
837+
// with the server.
838+
839+
engineCommandManager.engineConnection.state = {
840+
type: EngineConnectionStateType.ConnectionEstablished,
841+
}
842+
843+
engineCommandManager.inSequence = 1
844+
845+
engineCommandManager.engineConnection.dispatchEvent(
846+
new CustomEvent(EngineConnectionEvents.Opened, {
847+
detail: engineCommandManager.engineConnection,
848+
})
849+
)
850+
markOnce('code/endInitialEngineConnect')
827851
}
828852
this.unreliableDataChannel?.addEventListener(
829853
'open',
@@ -1509,15 +1533,30 @@ export class EngineCommandManager extends EventTarget {
15091533

15101534
// eslint-disable-next-line @typescript-eslint/no-misused-promises
15111535
this.onEngineConnectionOpened = async () => {
1512-
await this.rustContext?.clearSceneAndBustCache(
1513-
{ settings: await jsAppSettings() },
1514-
this.codeManager?.currentFilePath || undefined
1515-
)
1536+
console.log("onEngineConnectionOpened")
1537+
1538+
try {
1539+
console.log("clearing scene and busting cache")
1540+
await this.rustContext?.clearSceneAndBustCache(
1541+
{ settings: await jsAppSettings() },
1542+
this.codeManager?.currentFilePath || undefined
1543+
)
1544+
} catch(e) {
1545+
// If this happens shit's actually gone south aka the websocket closed.
1546+
// Let's restart.
1547+
console.warn("shit's gone south")
1548+
console.warn(e)
1549+
this.engineConnection?.dispatchEvent(
1550+
new CustomEvent(EngineConnectionEvents.RestartRequest, {})
1551+
)
1552+
return
1553+
}
15161554

15171555
// Set the stream's camera projection type
15181556
// We don't send a command to the engine if in perspective mode because
15191557
// for now it's the engine's default.
15201558
if (settings.cameraProjection === 'orthographic') {
1559+
console.log("Setting camera to orthographic")
15211560
this.sendSceneCommand({
15221561
type: 'modeling_cmd_req',
15231562
cmd_id: uuidv4(),
@@ -1528,15 +1567,18 @@ export class EngineCommandManager extends EventTarget {
15281567
}
15291568

15301569
// Set the theme
1570+
console.log("Setting theme")
15311571
this.setTheme(this.settings.theme).catch(reportRejection)
15321572
// Set up a listener for the dark theme media query
1573+
console.log("Setup theme media query change")
15331574
darkModeMatcher?.addEventListener(
15341575
'change',
15351576
this.onDarkThemeMediaQueryChange
15361577
)
15371578

15381579
// Set the edge lines visibility
15391580
// eslint-disable-next-line @typescript-eslint/no-floating-promises
1581+
console.log("setting edge_lines_visible")
15401582
this.sendSceneCommand({
15411583
type: 'modeling_cmd_req',
15421584
cmd_id: uuidv4(),
@@ -1546,9 +1588,11 @@ export class EngineCommandManager extends EventTarget {
15461588
},
15471589
})
15481590

1591+
console.log("camControlsCameraChange")
15491592
this._camControlsCameraChange()
15501593

15511594
// eslint-disable-next-line @typescript-eslint/no-floating-promises
1595+
console.log("call default_camera_get_settings")
15521596
this.sendSceneCommand({
15531597
// CameraControls subscribes to default_camera_get_settings response events
15541598
// firing this at connection ensure the camera's are synced initially
@@ -1561,6 +1605,7 @@ export class EngineCommandManager extends EventTarget {
15611605

15621606
setIsStreamReady(true)
15631607

1608+
console.log("Dispatching SceneReady")
15641609
// Other parts of the application should use this to react on scene ready.
15651610
this.dispatchEvent(
15661611
new CustomEvent(EngineCommandManagerEvents.SceneReady, {

0 commit comments

Comments
 (0)