@ksp-gonogo/kerbcast 1.4.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.
@@ -0,0 +1,488 @@
1
+ /*
2
+ Generated by typeshare 1.13.4
3
+ */
4
+
5
+ export interface AdaptiveShedPayload {
6
+ level: number;
7
+ kspFps: number;
8
+ reason: string;
9
+ }
10
+
11
+ /**
12
+ * Lifecycle state of a camera. Transmitted in `CameraState` so clients
13
+ * can react to part destruction without a new message type.
14
+ *
15
+ * Destroyed is a terminal state — the sidecar never transitions a camera
16
+ * back to Active after writing the tombstone.
17
+ */
18
+ export enum CameraLifecycle {
19
+ Active = "active",
20
+ Destroyed = "destroyed",
21
+ }
22
+
23
+ /**
24
+ * Layer mask. Mirrors `Kerbcast.CameraLayers` on the plugin side.
25
+ * Receiving clients use this for both the rendered-layer status reports
26
+ * and per-camera layer requests.
27
+ */
28
+ export enum Layer {
29
+ Near = "NEAR",
30
+ Scaled = "SCALED",
31
+ Far = "FAR",
32
+ Galaxy = "GALAXY",
33
+ }
34
+
35
+ /**
36
+ * Viewer-selectable resolution preset. Each maps to a fraction of the
37
+ * operator-configured render size (the settings.cfg Width/Height ceiling):
38
+ * full = 1.0, threeQuarter = 0.75, half = 0.5, quarter = 0.25. The steps
39
+ * mirror the plugin's shed-table resolution ladder so the viewer clamp and
40
+ * the adaptive controller move on the same grid, and a preset can never
41
+ * exceed the ring's allocated max (every scale is <= 1.0 of the ceiling).
42
+ *
43
+ * Presets, not freeform WxH: a fixed menu keeps the unauthenticated
44
+ * viewer-writable surface tiny and aspect-correct by construction.
45
+ */
46
+ export enum QualityPreset {
47
+ Full = "full",
48
+ ThreeQuarter = "threeQuarter",
49
+ Half = "half",
50
+ Quarter = "quarter",
51
+ }
52
+
53
+ /**
54
+ * Per-camera snapshot pushed by the sidecar on every state change
55
+ * (operator API call, adaptive shed, vessel change). Same shape served
56
+ * by `GET /cameras` so client UIs can treat the two interchangeably.
57
+ *
58
+ * Capability fields (`supports_zoom`, `supports_pan`) let clients
59
+ * render controls only for features each part actually offers — a
60
+ * fixed-FoV camera shouldn't get a zoom slider, a non-steerable one
61
+ * shouldn't get a pan stick.
62
+ */
63
+ export interface CameraState {
64
+ flightId: number;
65
+ /**
66
+ * Part-destruction lifecycle. `Active` for live cameras. `Destroyed`
67
+ * when the plugin reports the Hullcam part was destroyed in-flight
68
+ * (collision, overheating, decoupling beyond physics range). Once
69
+ * destroyed the sidecar stops forwarding frames; the client should
70
+ * surface a "SIGNAL LOST" overlay and keep the last frame visible.
71
+ */
72
+ lifecycle?: CameraLifecycle;
73
+ partName: string;
74
+ partTitle: string;
75
+ cameraName: string;
76
+ vesselName: string;
77
+ /** Currently-rendered layers (effective after adaptive shedding). */
78
+ layers: Layer[];
79
+ /**
80
+ * Operator-requested layer mask — the ceiling adaptive shedding
81
+ * can reduce below. UIs use this to show "scaled is *requested* but
82
+ * not *active* right now due to fps".
83
+ */
84
+ operatorLayers: Layer[];
85
+ /**
86
+ * Currently-rendered dimensions (effective after adaptive
87
+ * resolution downscale). Equal to `operatorWidth` × `operatorHeight`
88
+ * when no auto-shed is in effect.
89
+ */
90
+ renderWidth: number;
91
+ renderHeight: number;
92
+ /**
93
+ * Operator-requested render size — ceiling adaptive downscale can
94
+ * reduce below.
95
+ */
96
+ operatorWidth: number;
97
+ operatorHeight: number;
98
+ /**
99
+ * Whether the part's Hullcam module supports runtime FoV changes
100
+ * (i.e. it's a `MuMechModuleHullCameraZoom`, not the fixed base
101
+ * `MuMechModuleHullCamera`). 19 of 21 stock parts do.
102
+ */
103
+ supportsZoom: boolean;
104
+ /** Current effective FoV in degrees. */
105
+ fov: number;
106
+ /**
107
+ * FoV bounds the operator can choose between. Wider than the
108
+ * camera's "default" — these come from the Hullcam part config.
109
+ * Equal to `fov` when `supports_zoom == false`.
110
+ */
111
+ fovMin: number;
112
+ fovMax: number;
113
+ /**
114
+ * Whether the part supports pan/tilt (kerbcast-side mod extension —
115
+ * no stock Hullcam parts are steerable, but the extended mod adds
116
+ * pan to specific parts). False on every shipping part today;
117
+ * clients should hide pan controls until this flips true.
118
+ */
119
+ supportsPan: boolean;
120
+ panYaw: number;
121
+ panPitch: number;
122
+ panYawMin: number;
123
+ panYawMax: number;
124
+ panPitchMin: number;
125
+ panPitchMax: number;
126
+ /**
127
+ * Current encoder bitrate target in bits per second. Equals
128
+ * `target_bitrate_bps` after the consume loop applies receiver
129
+ * feedback; the two diverge briefly between a REMB arriving and
130
+ * the consume loop's next tick. Zero when no encoder is running
131
+ * yet (no subscribers).
132
+ */
133
+ encoderBitrateBps: number;
134
+ /**
135
+ * Bandwidth target derived from receivers' REMB feedback (min
136
+ * across active subscribers). Drives the encoder when
137
+ * significantly diverged. Zero until the first REMB packet
138
+ * arrives — encoder falls back to the sidecar's CLI default.
139
+ */
140
+ targetBitrateBps: number;
141
+ /**
142
+ * Effective degrade level (max across subscribers' SetDegrade
143
+ * requests). 0.0 = perfect, 1.0 = max degradation. Applied
144
+ * alongside operator render-size + adaptive shed; the encoder
145
+ * multiplies its effective bitrate by `(1 - 0.7 * level)` and
146
+ * skips fan-out for a fraction of frames at high levels.
147
+ */
148
+ degradeLevel: number;
149
+ /**
150
+ * Viewer-requested resolution preset for this camera. Last write
151
+ * wins across all connected peers; this broadcast is what keeps
152
+ * every UI consistent. Absent/None = auto: no viewer clamp, the
153
+ * operator ceiling and the adaptive controller alone decide.
154
+ */
155
+ viewerQuality?: QualityPreset;
156
+ /**
157
+ * Why the effective resolution sits below what the viewer asked
158
+ * for (or below the operator ceiling when no preset is set).
159
+ * `"throttled"` = the adaptive perf machinery is holding the
160
+ * camera down; the viewer target is honored again on recovery.
161
+ * Absent/None = the request is fully honored.
162
+ */
163
+ qualityLimitedBy?: string;
164
+ }
165
+
166
+ export interface CameraSnapshotPayload {
167
+ cameras: CameraState[];
168
+ }
169
+
170
+ export interface CameraStateChangedPayload {
171
+ state: CameraState;
172
+ }
173
+
174
+ export enum ErrorSource {
175
+ Sidecar = "sidecar",
176
+ Client = "client",
177
+ }
178
+
179
+ export interface ErrorPayload {
180
+ message: string;
181
+ source?: ErrorSource;
182
+ }
183
+
184
+ export interface FlightIdPayload {
185
+ flightId: number;
186
+ }
187
+
188
+ export interface HelloPayload {
189
+ sidecarVersion: string;
190
+ encoderBackend: string;
191
+ }
192
+
193
+ export interface SetDegradePayload {
194
+ flightId: number;
195
+ /**
196
+ * 0.0 = perfect quality, 1.0 = maximum degradation. Caps and
197
+ * is per-subscriber: the sidecar applies max across active
198
+ * subscribers so the noisiest consumer's request wins (same
199
+ * pattern as REMB picking the min bandwidth).
200
+ */
201
+ level: number;
202
+ }
203
+
204
+ export interface SetFovPayload {
205
+ flightId: number;
206
+ fov: number;
207
+ }
208
+
209
+ export interface SetLayersPayload {
210
+ flightId: number;
211
+ layers: Layer[];
212
+ }
213
+
214
+ export interface SetPanPayload {
215
+ flightId: number;
216
+ yaw: number;
217
+ pitch: number;
218
+ }
219
+
220
+ export interface SetPanRatePayload {
221
+ flightId: number;
222
+ /**
223
+ * Normalised yaw velocity, -1..=1. +1 = pan right at the part's
224
+ * full `PanRateDegPerSec`. Persistent until superseded (a new rate,
225
+ * including zero, replaces it); the plugin integrates it into the
226
+ * pan target every frame.
227
+ */
228
+ yawRate: number;
229
+ /**
230
+ * Normalised pitch velocity, -1..=1. +1 = pan up. Persistent until
231
+ * superseded.
232
+ */
233
+ pitchRate: number;
234
+ }
235
+
236
+ export interface SetQualityPayload {
237
+ flightId: number;
238
+ /**
239
+ * Requested resolution preset, or absent/null for auto (clear the
240
+ * viewer clamp). Future viewer-quality knobs are added here as
241
+ * optional fields with serde defaults, so older clients keep
242
+ * parsing; richer controls (bitrate, ladder toggles) stay
243
+ * operator-only until deliberately opened up.
244
+ */
245
+ preset?: QualityPreset;
246
+ }
247
+
248
+ export interface SetRenderSizePayload {
249
+ flightId: number;
250
+ width: number;
251
+ height: number;
252
+ }
253
+
254
+ export interface SetThrottleMainScreenPayload {
255
+ /**
256
+ * When `true`, disable the KSP main flight cameras to free GPU for
257
+ * kerbcast streams. Persists via the per-save difficulty parameter,
258
+ * matching the in-game Difficulty Settings toggle.
259
+ */
260
+ enabled: boolean;
261
+ }
262
+
263
+ export interface SetZoomRatePayload {
264
+ flightId: number;
265
+ /**
266
+ * Normalised zoom velocity, -1..=1. +1 = zoom IN (FoV decreasing)
267
+ * at the part's full `ZoomRateDegPerSec`. Persistent until
268
+ * superseded; the plugin integrates it into the FoV target every
269
+ * frame.
270
+ */
271
+ rate: number;
272
+ }
273
+
274
+ export interface SettingsStatePayload {
275
+ /**
276
+ * Effective value of the "Throttle KSP main render" setting as last
277
+ * reported by the plugin's `global.status.json`. Reflects what the
278
+ * plugin has *applied*, not just what was last requested.
279
+ */
280
+ throttleMainScreen: boolean;
281
+ /**
282
+ * KSP mission time (`Planetarium.GetUniversalTime()`, seconds) at
283
+ * which the video currently being produced was captured. `None` =
284
+ * no clock yet (old plugin/sidecar, or first status not seen); a
285
+ * consumer treats that as "unknown" and keeps a live passthrough.
286
+ */
287
+ captureUt?: number;
288
+ /**
289
+ * Monotonic counter bumped only on a non-monotonic UT jump (revert,
290
+ * quickload, scene reload). Consumers resynchronise on any change;
291
+ * the absolute value is meaningless.
292
+ */
293
+ captureEpoch?: number;
294
+ /**
295
+ * Current KSP time-warp multiplier, so a consumer can interpolate
296
+ * `capture_ut` between ~1Hz samples. `None` => treat as 1.0.
297
+ */
298
+ timeWarpRate?: number;
299
+ }
300
+
301
+ /**
302
+ * Slot↔camera assignment for the dynamic-subscription model. The sidecar
303
+ * negotiates a pool of recv-only video transceivers up front; `Subscribe`
304
+ * binds a camera to a free one and the sidecar announces the binding here.
305
+ */
306
+ export interface SlotMapPayload {
307
+ /**
308
+ * Transceiver `mid` carrying this slot's track. Stable for the
309
+ * connection's life, so the client keys off it rather than the
310
+ * fragile `onTrack` arrival order — match against
311
+ * `RTCTrackEvent.transceiver.mid`.
312
+ */
313
+ mid: string;
314
+ /**
315
+ * Camera now carried by the slot, or `None` when the slot was freed
316
+ * by `Unsubscribe`. Serialised as `null` (not omitted) so "freed" is
317
+ * unambiguous on the wire.
318
+ */
319
+ flightId?: number;
320
+ }
321
+
322
+ /** Messages sent FROM the client TO the sidecar. */
323
+ export type ClientMessage =
324
+ /**
325
+ * First message on a new data channel. Sidecar replies with `Hello`
326
+ * + a `CameraSnapshot` so the client gets server version + every
327
+ * camera's full state on one round-trip.
328
+ */
329
+ | { type: "hello", content?: undefined }
330
+ /**
331
+ * Override the operator layer mask for one camera. Server-wide
332
+ * state — all consumers see the change.
333
+ */
334
+ | { type: "set-layers", content: SetLayersPayload }
335
+ /**
336
+ * Override the operator render size for one camera. Even pixels
337
+ * only (H.264 chroma); server caps at the ring's allocated max.
338
+ */
339
+ | { type: "set-render-size", content: SetRenderSizePayload }
340
+ /**
341
+ * Set the camera's field-of-view (degrees). Silently ignored for
342
+ * parts whose Hullcam module is the fixed base (`supportsZoom ==
343
+ * false`); clients are expected to clamp to `fovMin / fovMax`
344
+ * from the camera's `CameraState` before sending.
345
+ */
346
+ | { type: "set-fov", content: SetFovPayload }
347
+ /**
348
+ * Pan/tilt the camera (yaw and pitch, both degrees from the
349
+ * part's resting forward). No stock Hullcam parts support this
350
+ * yet — the message is ignored until the planned mod extension
351
+ * adds steerable mounts to specific parts. Clients should hide
352
+ * pan controls until `supportsPan == true` for the camera.
353
+ */
354
+ | { type: "set-pan", content: SetPanPayload }
355
+ /**
356
+ * Set a *persistent* pan/tilt velocity (normalised yaw + pitch,
357
+ * -1..=1). Unlike `SetPan` (a one-shot absolute target), the rate
358
+ * holds until a new rate — including zero, which stops — supersedes
359
+ * it; the plugin integrates it into the pan target every frame so
360
+ * smoothness lives in KSP's frame loop rather than the control-poll
361
+ * cadence. +yaw = pan right, +pitch = pan up. Ignored (sidecar
362
+ * replies `Error`) when `supportsPan == false`. Composes with
363
+ * `SetPan`: an absolute command jumps the target, integration then
364
+ * continues from there.
365
+ */
366
+ | { type: "set-pan-rate", content: SetPanRatePayload }
367
+ /**
368
+ * Set a *persistent* zoom velocity (normalised, -1..=1). +1 = zoom
369
+ * IN (FoV decreasing). Holds until a new rate — including zero —
370
+ * supersedes it; the plugin integrates it into the FoV target every
371
+ * frame. Ignored (sidecar replies `Error`) when `supportsZoom ==
372
+ * false`. Composes with the absolute `SetFov` the same way
373
+ * `SetPanRate` composes with `SetPan`.
374
+ */
375
+ | { type: "set-zoom-rate", content: SetZoomRatePayload }
376
+ /**
377
+ * Request artificial signal degradation. 0.0 = perfect quality,
378
+ * 1.0 = maximum degradation. Per-subscriber: the sidecar
379
+ * applies max across active subscribers (slowest consumer
380
+ * wins, same pattern as REMB bandwidth). Lets consumers signal
381
+ * to the sidecar "I want to look like the in-game CommNet
382
+ * antenna is struggling"; sidecar takes the opportunity to
383
+ * reduce bitrate + skip frames, which both creates the
384
+ * signal-loss aesthetic AND saves encoder CPU.
385
+ */
386
+ | { type: "set-degrade", content: SetDegradePayload }
387
+ /**
388
+ * Set (or clear, with `preset: null`) a camera's viewer-requested
389
+ * resolution preset. Server-wide, last write wins across peers; the
390
+ * resulting `CameraStateChanged` broadcast keeps every UI
391
+ * consistent. The effective resolution is always
392
+ * min(operator ceiling, adaptive level, viewer preset): a preset
393
+ * can never raise quality past the operator's settings.cfg
394
+ * Width/Height, and the adaptive controller's demotes keep
395
+ * winning until it recovers. Viewers can NOT change bitrate or
396
+ * toggle the adaptive machinery through this message.
397
+ */
398
+ | { type: "set-quality", content: SetQualityPayload }
399
+ /**
400
+ * Request an IDR (keyframe) on the next encode tick. Browsers send
401
+ * this when they've dropped enough frames to be unable to decode
402
+ * the current P-frame chain. Sidecar forwards to the camera's
403
+ * encoder.
404
+ */
405
+ | { type: "request-keyframe", content: FlightIdPayload }
406
+ /**
407
+ * Dynamically subscribe to a camera on an already-connected peer. The
408
+ * sidecar binds it to a free pre-negotiated slot (one of the recv-only
409
+ * video transceivers from the offer), starts encoding it, forces a
410
+ * keyframe, and replies with `SlotMap` naming the transceiver `mid` now
411
+ * carrying it — no renegotiation. Replies with `Error` if no slot is
412
+ * free (the client should have negotiated enough transceivers up front).
413
+ */
414
+ | { type: "subscribe", content: FlightIdPayload }
415
+ /**
416
+ * Release a camera's slot. The sidecar stops feeding that slot (the
417
+ * camera sleeps if it has no other subscribers) and replies with
418
+ * `SlotMap { flightId: null }` for the freed transceiver `mid`.
419
+ */
420
+ | { type: "unsubscribe", content: FlightIdPayload }
421
+ /** Response to `Ping`. Browser sends this immediately on receiving each Ping. */
422
+ | { type: "pong", content?: undefined }
423
+ /**
424
+ * Graceful teardown. The canonical way for a client to leave: the sidecar
425
+ * immediately releases every camera this peer is feeding (so they sleep
426
+ * without waiting for ICE to time out), after which the client may close
427
+ * the connection. The drop-detection reaper remains the fallback for
428
+ * ungraceful exits (crash / tab close / network loss) that can't send this.
429
+ */
430
+ | { type: "disconnect", content?: undefined }
431
+ /**
432
+ * Enable or disable the KSP main render throttle globally. Persists
433
+ * across saves (writes the per-save difficulty parameter). Server-wide:
434
+ * all peers see the resulting `SettingsState` broadcast.
435
+ */
436
+ | { type: "set-throttle-main-screen", content: SetThrottleMainScreenPayload };
437
+
438
+ /** Messages sent FROM the sidecar TO the client. */
439
+ export type ServerMessage =
440
+ /**
441
+ * Reply to `Hello`. Carries server version + the active encoder
442
+ * backend's name.
443
+ */
444
+ | { type: "hello", content: HelloPayload }
445
+ /**
446
+ * Full snapshot of every currently-attached camera. Sent in reply
447
+ * to `Hello` and on any structural change (vessel switch, ring
448
+ * added/removed).
449
+ */
450
+ | { type: "camera-snapshot", content: CameraSnapshotPayload }
451
+ /**
452
+ * Single-camera state change. Sent when an operator request applies
453
+ * or adaptive shedding kicks in. Lets clients update their UI
454
+ * without re-fetching the whole snapshot.
455
+ */
456
+ | { type: "camera-state-changed", content: CameraStateChangedPayload }
457
+ /**
458
+ * Slot↔camera assignment for the dynamic-subscription model. Sent in
459
+ * reply to `Subscribe`/`Unsubscribe`: `flightId` is the camera now
460
+ * carried by transceiver `mid`, or `null` when the slot was freed. The
461
+ * client maps `mid` to the track it received via `onTrack` and routes
462
+ * that track to (or away from) the camera's stream — no renegotiation.
463
+ */
464
+ | { type: "slot-map", content: SlotMapPayload }
465
+ /**
466
+ * Adaptive shedding level changed. Reason is human-readable
467
+ * ("ksp-fps-low", "ksp-fps-recovered") so client UIs can show
468
+ * *why* quality changed rather than just *that* it did.
469
+ */
470
+ | { type: "adaptive-shed", content: AdaptiveShedPayload }
471
+ /**
472
+ * Malformed / unknown client message. Includes the offending
473
+ * payload so the client can log it.
474
+ */
475
+ | { type: "error", content: ErrorPayload }
476
+ /**
477
+ * Keepalive probe sent by the sidecar every 5 seconds. Browser responds
478
+ * with `Pong`; if no Ping arrives within 15s the browser tears down
479
+ * the connection.
480
+ */
481
+ | { type: "ping", content?: undefined }
482
+ /**
483
+ * Current value of global settings (throttle state). Sent after `Hello`
484
+ * so a freshly-connected client shows correct state immediately, and
485
+ * re-broadcast whenever the polled plugin status shows a change.
486
+ */
487
+ | { type: "settings-state", content: SettingsStatePayload };
488
+