@ksp-gonogo/kerbcast 1.8.1 → 1.9.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.
@@ -22,6 +22,35 @@ export declare enum CameraKind {
22
22
  Part = "part",
23
23
  Kerbal = "kerbal"
24
24
  }
25
+ /**
26
+ * How hard a camera is currently being captured. Reported per camera in
27
+ * `CameraState` so a consumer renders what is actually happening rather than
28
+ * predicting it from delay arithmetic.
29
+ *
30
+ * `Background` and `Off` only occur when background capture is enabled; with it off
31
+ * (the default) a subscribed camera is `Full` and an unsubscribed one is `Off`,
32
+ * which is exactly today's behaviour under the new names.
33
+ */
34
+ export declare enum CaptureState {
35
+ /** Capturing at the primary rate: an ordinary live feed. */
36
+ Full = "full",
37
+ /**
38
+ * Promoted and on the way to the primary rate, but not there yet — either
39
+ * awaiting the forced keyframe or still rate-limited by the capture budget.
40
+ */
41
+ Ramping = "ramping",
42
+ /**
43
+ * Background capture: recent but sparse. A consumer should present this as
44
+ * a reduced-rate channel, not as a live feed.
45
+ */
46
+ Background = "background",
47
+ /**
48
+ * Not being captured at all — unsubscribed, or the background layer has
49
+ * been shed under load. A consumer must NOT keep presenting the last frame
50
+ * as though it were a live low-rate channel.
51
+ */
52
+ Off = "off"
53
+ }
25
54
  /**
26
55
  * Which source a kerbal camera is currently rendering: a seated IVA
27
56
  * portrait, or the kerbal's own view while on EVA. Only meaningful when
@@ -95,6 +124,11 @@ export interface CameraState {
95
124
  * Defaults to `Part` so existing payloads are unaffected.
96
125
  */
97
126
  kind?: CameraKind;
127
+ /**
128
+ * How hard this camera is currently being captured. Defaults to `Full` so
129
+ * existing payloads and plugins predating background capture are unaffected.
130
+ */
131
+ captureState?: CaptureState;
98
132
  /**
99
133
  * The kerbal's real `ProtoCrewMember.persistentID`: the stable key a
100
134
  * consumer correlates against. `None` for part cameras. Distinct from
@@ -248,6 +282,18 @@ export interface ReportDisplaySizePayload {
248
282
  export interface SceneStateChangedPayload {
249
283
  inFlight: boolean;
250
284
  }
285
+ /**
286
+ * Consumer's background-capture request. Global rather than
287
+ * per-camera: it is a statement about how much background cost this consumer
288
+ * wants spent, not about one camera. Zero stops asking.
289
+ */
290
+ export interface SetBackgroundCaptureFpsPayload {
291
+ /**
292
+ * Requested rate in fps for cameras this consumer is not displaying.
293
+ * Clamped to the operator ceiling; 0 means no background capture.
294
+ */
295
+ fps: number;
296
+ }
251
297
  export interface SetDegradePayload {
252
298
  flightId: number;
253
299
  /**
@@ -458,6 +504,31 @@ export type ClientMessage =
458
504
  type: "set-force-full-resolution";
459
505
  content: SetForceFullResolutionPayload;
460
506
  }
507
+ /**
508
+ * Ask for background capture: subscribed cameras that this
509
+ * consumer is NOT displaying keep capturing at a low rate instead of
510
+ * stopping, so switching to one promotes an already-flowing stream rather
511
+ * than cold-starting from black.
512
+ *
513
+ * Per-consumer and MAX-aggregated, like `ReportDisplaySize`: it runs
514
+ * at the highest rate any connected consumer asked for, and relaxes when
515
+ * they lower it or disconnect. A consumer that never sends this pays
516
+ * nothing, which is why a stock install needs no configuration to serve
517
+ * both a plain viewer and a consumer that wants background capture.
518
+ *
519
+ * The rate is a REQUEST, not a guarantee. It is clamped to the operator's
520
+ * `BackgroundCaptureFps` ceiling and is the first thing dropped when the
521
+ * game approaches its framerate floor — watch `CameraState.capture_state`
522
+ * for what is actually happening. Send 0 to stop asking.
523
+ *
524
+ * Receiving the frames is not the same as keeping them: a consumer that
525
+ * wants history for a camera it isn't showing has to buffer the background
526
+ * feed itself.
527
+ */
528
+ | {
529
+ type: "set-background-capture-fps";
530
+ content: SetBackgroundCaptureFpsPayload;
531
+ }
461
532
  /**
462
533
  * Set the camera's field-of-view (degrees). Silently ignored for
463
534
  * parts whose Hullcam module is the fixed base (`supportsZoom ==
@@ -22,6 +22,36 @@ export var CameraKind;
22
22
  CameraKind["Part"] = "part";
23
23
  CameraKind["Kerbal"] = "kerbal";
24
24
  })(CameraKind || (CameraKind = {}));
25
+ /**
26
+ * How hard a camera is currently being captured. Reported per camera in
27
+ * `CameraState` so a consumer renders what is actually happening rather than
28
+ * predicting it from delay arithmetic.
29
+ *
30
+ * `Background` and `Off` only occur when background capture is enabled; with it off
31
+ * (the default) a subscribed camera is `Full` and an unsubscribed one is `Off`,
32
+ * which is exactly today's behaviour under the new names.
33
+ */
34
+ export var CaptureState;
35
+ (function (CaptureState) {
36
+ /** Capturing at the primary rate: an ordinary live feed. */
37
+ CaptureState["Full"] = "full";
38
+ /**
39
+ * Promoted and on the way to the primary rate, but not there yet — either
40
+ * awaiting the forced keyframe or still rate-limited by the capture budget.
41
+ */
42
+ CaptureState["Ramping"] = "ramping";
43
+ /**
44
+ * Background capture: recent but sparse. A consumer should present this as
45
+ * a reduced-rate channel, not as a live feed.
46
+ */
47
+ CaptureState["Background"] = "background";
48
+ /**
49
+ * Not being captured at all — unsubscribed, or the background layer has
50
+ * been shed under load. A consumer must NOT keep presenting the last frame
51
+ * as though it were a live low-rate channel.
52
+ */
53
+ CaptureState["Off"] = "off";
54
+ })(CaptureState || (CaptureState = {}));
25
55
  /**
26
56
  * Which source a kerbal camera is currently rendering: a seated IVA
27
57
  * portrait, or the kerbal's own view while on EVA. Only meaningful when
package/dist/client.d.ts CHANGED
@@ -474,6 +474,31 @@ export declare class KerbcastClient extends TypedEmitter<KerbcastClientEvents> {
474
474
  * feed primitives self-measure and call this; consumers do not report by hand.
475
475
  */
476
476
  reportDisplaySize(flightId: number, width: number, height: number): Promise<void>;
477
+ /**
478
+ * Ask for background capture: subscribed cameras this consumer is NOT
479
+ * displaying keep capturing at a low rate instead of stopping, so switching
480
+ * to one promotes an already-flowing stream rather than cold-starting from
481
+ * black.
482
+ *
483
+ * Opt-in and per-consumer, aggregated MAX-across-consumers. A consumer that
484
+ * never calls this pays nothing, which is why the same kerbcast install
485
+ * serves both a plain viewer and a consumer that wants background capture
486
+ * with no configuration either side. Pass 0 to stop asking; the request is
487
+ * dropped automatically on disconnect.
488
+ *
489
+ * The rate is a REQUEST, not a guarantee: it is capped by the operator's
490
+ * `BackgroundCaptureFps` ceiling and is the first thing dropped as the game
491
+ * approaches its framerate floor. Read `CameraState.captureState` for what is
492
+ * actually happening rather than assuming this took effect — it reports
493
+ * `Background` only while the reduced rate is genuinely running, and `Off`
494
+ * once the layer has been shed.
495
+ *
496
+ * Receiving background frames is not the same as keeping them. A consumer
497
+ * that wants *history* for a camera it isn't showing — to render instantly on
498
+ * switch under a simulated signal delay, say — has to buffer those feeds
499
+ * itself; background capture only makes the frames available to buffer.
500
+ */
501
+ setBackgroundCaptureFps(fps: number): Promise<void>;
477
502
  /**
478
503
  * Ask a pan+zoom camera to auto-track a moving vessel (or stop). Sends the
479
504
  * intent; the sidecar holds the chosen mode per camera and publishes it in
package/dist/client.js CHANGED
@@ -712,6 +712,33 @@ export class KerbcastClient extends TypedEmitter {
712
712
  async reportDisplaySize(flightId, width, height) {
713
713
  await this._send({ type: "report-display-size", content: { flightId, width, height } });
714
714
  }
715
+ /**
716
+ * Ask for background capture: subscribed cameras this consumer is NOT
717
+ * displaying keep capturing at a low rate instead of stopping, so switching
718
+ * to one promotes an already-flowing stream rather than cold-starting from
719
+ * black.
720
+ *
721
+ * Opt-in and per-consumer, aggregated MAX-across-consumers. A consumer that
722
+ * never calls this pays nothing, which is why the same kerbcast install
723
+ * serves both a plain viewer and a consumer that wants background capture
724
+ * with no configuration either side. Pass 0 to stop asking; the request is
725
+ * dropped automatically on disconnect.
726
+ *
727
+ * The rate is a REQUEST, not a guarantee: it is capped by the operator's
728
+ * `BackgroundCaptureFps` ceiling and is the first thing dropped as the game
729
+ * approaches its framerate floor. Read `CameraState.captureState` for what is
730
+ * actually happening rather than assuming this took effect — it reports
731
+ * `Background` only while the reduced rate is genuinely running, and `Off`
732
+ * once the layer has been shed.
733
+ *
734
+ * Receiving background frames is not the same as keeping them. A consumer
735
+ * that wants *history* for a camera it isn't showing — to render instantly on
736
+ * switch under a simulated signal delay, say — has to buffer those feeds
737
+ * itself; background capture only makes the frames available to buffer.
738
+ */
739
+ async setBackgroundCaptureFps(fps) {
740
+ await this._send({ type: "set-background-capture-fps", content: { fps } });
741
+ }
715
742
  /**
716
743
  * Ask a pan+zoom camera to auto-track a moving vessel (or stop). Sends the
717
744
  * intent; the sidecar holds the chosen mode per camera and publishes it in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ksp-gonogo/kerbcast",
3
- "version": "1.8.1",
3
+ "version": "1.9.0",
4
4
  "description": "TypeScript client for the kerbcast KSP camera-streaming sidecar. KerbcastClient wraps the WebRTC peer + control channel; wire-format types are typeshare-generated from the sidecar's Rust protocol module.",
5
5
  "license": "MIT",
6
6
  "author": "Jon Pepler",
@@ -29,6 +29,36 @@ export enum CameraKind {
29
29
  Kerbal = "kerbal",
30
30
  }
31
31
 
32
+ /**
33
+ * How hard a camera is currently being captured. Reported per camera in
34
+ * `CameraState` so a consumer renders what is actually happening rather than
35
+ * predicting it from delay arithmetic.
36
+ *
37
+ * `Background` and `Off` only occur when background capture is enabled; with it off
38
+ * (the default) a subscribed camera is `Full` and an unsubscribed one is `Off`,
39
+ * which is exactly today's behaviour under the new names.
40
+ */
41
+ export enum CaptureState {
42
+ /** Capturing at the primary rate: an ordinary live feed. */
43
+ Full = "full",
44
+ /**
45
+ * Promoted and on the way to the primary rate, but not there yet — either
46
+ * awaiting the forced keyframe or still rate-limited by the capture budget.
47
+ */
48
+ Ramping = "ramping",
49
+ /**
50
+ * Background capture: recent but sparse. A consumer should present this as
51
+ * a reduced-rate channel, not as a live feed.
52
+ */
53
+ Background = "background",
54
+ /**
55
+ * Not being captured at all — unsubscribed, or the background layer has
56
+ * been shed under load. A consumer must NOT keep presenting the last frame
57
+ * as though it were a live low-rate channel.
58
+ */
59
+ Off = "off",
60
+ }
61
+
32
62
  /**
33
63
  * Which source a kerbal camera is currently rendering: a seated IVA
34
64
  * portrait, or the kerbal's own view while on EVA. Only meaningful when
@@ -106,6 +136,11 @@ export interface CameraState {
106
136
  * Defaults to `Part` so existing payloads are unaffected.
107
137
  */
108
138
  kind?: CameraKind;
139
+ /**
140
+ * How hard this camera is currently being captured. Defaults to `Full` so
141
+ * existing payloads and plugins predating background capture are unaffected.
142
+ */
143
+ captureState?: CaptureState;
109
144
  /**
110
145
  * The kerbal's real `ProtoCrewMember.persistentID`: the stable key a
111
146
  * consumer correlates against. `None` for part cameras. Distinct from
@@ -268,6 +303,19 @@ export interface SceneStateChangedPayload {
268
303
  inFlight: boolean;
269
304
  }
270
305
 
306
+ /**
307
+ * Consumer's background-capture request. Global rather than
308
+ * per-camera: it is a statement about how much background cost this consumer
309
+ * wants spent, not about one camera. Zero stops asking.
310
+ */
311
+ export interface SetBackgroundCaptureFpsPayload {
312
+ /**
313
+ * Requested rate in fps for cameras this consumer is not displaying.
314
+ * Clamped to the operator ceiling; 0 means no background capture.
315
+ */
316
+ fps: number;
317
+ }
318
+
271
319
  export interface SetDegradePayload {
272
320
  flightId: number;
273
321
  /**
@@ -473,6 +521,28 @@ export type ClientMessage =
473
521
  * term, never the perf-protection ceiling.
474
522
  */
475
523
  | { type: "set-force-full-resolution", content: SetForceFullResolutionPayload }
524
+ /**
525
+ * Ask for background capture: subscribed cameras that this
526
+ * consumer is NOT displaying keep capturing at a low rate instead of
527
+ * stopping, so switching to one promotes an already-flowing stream rather
528
+ * than cold-starting from black.
529
+ *
530
+ * Per-consumer and MAX-aggregated, like `ReportDisplaySize`: it runs
531
+ * at the highest rate any connected consumer asked for, and relaxes when
532
+ * they lower it or disconnect. A consumer that never sends this pays
533
+ * nothing, which is why a stock install needs no configuration to serve
534
+ * both a plain viewer and a consumer that wants background capture.
535
+ *
536
+ * The rate is a REQUEST, not a guarantee. It is clamped to the operator's
537
+ * `BackgroundCaptureFps` ceiling and is the first thing dropped when the
538
+ * game approaches its framerate floor — watch `CameraState.capture_state`
539
+ * for what is actually happening. Send 0 to stop asking.
540
+ *
541
+ * Receiving the frames is not the same as keeping them: a consumer that
542
+ * wants history for a camera it isn't showing has to buffer the background
543
+ * feed itself.
544
+ */
545
+ | { type: "set-background-capture-fps", content: SetBackgroundCaptureFpsPayload }
476
546
  /**
477
547
  * Set the camera's field-of-view (degrees). Silently ignored for
478
548
  * parts whose Hullcam module is the fixed base (`supportsZoom ==
package/src/client.ts CHANGED
@@ -1127,6 +1127,34 @@ export class KerbcastClient extends TypedEmitter<KerbcastClientEvents> {
1127
1127
  await this._send({ type: "report-display-size", content: { flightId, width, height } });
1128
1128
  }
1129
1129
 
1130
+ /**
1131
+ * Ask for background capture: subscribed cameras this consumer is NOT
1132
+ * displaying keep capturing at a low rate instead of stopping, so switching
1133
+ * to one promotes an already-flowing stream rather than cold-starting from
1134
+ * black.
1135
+ *
1136
+ * Opt-in and per-consumer, aggregated MAX-across-consumers. A consumer that
1137
+ * never calls this pays nothing, which is why the same kerbcast install
1138
+ * serves both a plain viewer and a consumer that wants background capture
1139
+ * with no configuration either side. Pass 0 to stop asking; the request is
1140
+ * dropped automatically on disconnect.
1141
+ *
1142
+ * The rate is a REQUEST, not a guarantee: it is capped by the operator's
1143
+ * `BackgroundCaptureFps` ceiling and is the first thing dropped as the game
1144
+ * approaches its framerate floor. Read `CameraState.captureState` for what is
1145
+ * actually happening rather than assuming this took effect — it reports
1146
+ * `Background` only while the reduced rate is genuinely running, and `Off`
1147
+ * once the layer has been shed.
1148
+ *
1149
+ * Receiving background frames is not the same as keeping them. A consumer
1150
+ * that wants *history* for a camera it isn't showing — to render instantly on
1151
+ * switch under a simulated signal delay, say — has to buffer those feeds
1152
+ * itself; background capture only makes the frames available to buffer.
1153
+ */
1154
+ async setBackgroundCaptureFps(fps: number): Promise<void> {
1155
+ await this._send({ type: "set-background-capture-fps", content: { fps } });
1156
+ }
1157
+
1130
1158
  /**
1131
1159
  * Ask a pan+zoom camera to auto-track a moving vessel (or stop). Sends the
1132
1160
  * intent; the sidecar holds the chosen mode per camera and publishes it in