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