Built the missing piece that connects the ksp-bridge to a real KSP instance via kRPC. This adds a typed service client on top of the existing KRPCClient, plus the SpaceCenter-specific extraction logic that pulls the universe state from a running KSP save. @kerbal-rt/krpc-client - types.ts — runtime representation of kRPC Type descriptors (TypeCode enum, KrpcType interface, decodeKrpcType, typeName) - decoder.ts — kRPC value codec: primitive decode/encode, class refs, enums, collections (LIST/SET/TUPLE/DICTIONARY), system messages (STATUS/SERVICES/STREAM/EVENT/PROCEDURE_CALL). 34 unit tests. - services.ts — ServiceCache built from KRPC.GetServices() response. Lookup by (service, procedure), enum value/name resolution. 12 tests. - service-client.ts — KrpcServices: high-level invoke-by-name client. loadServices() helper to connect + load catalog. 9 integration tests with a mock kRPC server. - schema.ts — added Set/Dictionary/Event/Expression types so the decoder can handle system messages without external .proto files. Also fixed a bug where 'STREAM' was being encoded as 0 due to protobufjs's nested-enum string lookup. ksp-bridge - extract.ts — the actual SpaceCenter calls. ~280 procedure calls per poll for a typical KSP save (UT, bodies, vessels, then per-body and per-vessel class methods in parallel). Build the UniverseSnapshot. - krpc-adapter.ts — rewrote to use KrpcServices (typed) instead of the stub extract function. Supports an optional injected services for testing. - bridge.ts — uses the new ExtractedState type and buildSnapshot. - index.ts — connects to kRPC; falls back to mock mode if no server. - convert.ts — backward-compat shim, re-exports from extract.ts. The ksp-bridge can now talk to a real KSP install. We do NOT need the kRPC mod's .proto files on disk — the server's GetServices() response is the source of truth for type info. Documented the full list of procedures we call, the kRPC value encoding, and the new architecture in ksp/README.md. Tests: 119 total, all green. Typecheck and build clean across all 11 projects. Bonus: fixed an integer-overflow bug in the krpc-client connect() handshake (was passing 'RPC'/'STREAM' strings to protobufjs; its nested-enum string lookup silently encodes as 0, which made the stream handshake send the wrong type. Switched to numeric codes.)
7.4 KiB
KSP ↔ kRPC integration
The ksp-bridge app connects to a running KSP instance via the
kRPC mod and pushes state to the
kerbal-rt API. This README documents what's wired up today and what's
still TODO.
Quick start
A. Mock mode (no KSP needed)
cd apps/tools/ksp-bridge
KERBAL_RT_API_URL=http://localhost:4000 \
BRIDGE_POLL_MS=500 \
pnpm start
The bridge starts, tries to connect to 127.0.0.1:50000, fails (no
kRPC server running), and falls back to MOCK mode: it emits synthetic
state every poll. This is great for verifying the HTTP pipeline and
the live-map / hub end-to-end without KSP.
B. With real KSP
-
Install KSP 1.12.5 (this is the version kRPC 0.5.x targets).
-
Install CKAN.
-
From CKAN, install:
kRPC(the mod itself, by djungelorm)- Any other mods you want
-
Launch KSP, start a save, and press Alt+F12 to open the kRPC server window. Make sure the RPC server is on
127.0.0.1:50000and the Stream server is on127.0.0.1:50001. -
Run the bridge:
cd apps/tools/ksp-bridge KERBAL_RT_API_URL=http://localhost:4000 \ KSP_KRPC_HOST=127.0.0.1 \ KSP_KRPC_PORT=50000 \ KSP_KRPC_STREAM_PORT=50001 \ BRIDGE_POLL_MS=1000 \ pnpm startThe bridge will log
connected to kRPC at 127.0.0.1:50000 — running with real KSP stateand start polling.
Architecture
Two layers
-
@kerbal-rt/krpc-client— the low-level kRPC client.- TCP connection (RPC port + stream port)
- Length-prefixed protobuf framing
- Connection handshake (
ConnectionRequest/ConnectionResponse) - Procedure invocation (
Request/Response) - Stream subscription (
AddStream/StreamUpdate) - Plus a typed service client built on top:
- Loads the service catalog via
KRPC.GetServices()on connect - Encodes procedure arguments based on the cached type info
- Decodes return values based on the cached type info
- Knows about the kRPC value encoding (primitives, classes, enums, collections, system messages)
- Loads the service catalog via
-
apps/tools/ksp-bridge— the actual KSP bridge.krpc-adapter.ts— owns the KRPCClient + KrpcServices, exposesconnect()/readState()/disconnect()extract.ts— calls SpaceCenter methods to read the full universe state and produces aUniverseSnapshotbridge.ts— the polling loop, HTTP POST to API, retry / reconnectindex.ts— entrypoint; falls back to MOCK mode if no kRPC server
No .proto files needed
We do not need the kRPC mod's .proto files on disk. The kRPC
server provides the full service catalog (procedures, classes, enums,
exceptions) via KRPC.GetServices() on connect, and we cache that into
a ServiceCache for lookups. The value encoding is implemented in
packages/krpc-client/src/decoder.ts.
The original plan (Phase 1c) called for loading the .proto files
with protobufjs.loadSync(). We pivoted to the GetServices approach
because:
- The kRPC server is the source of truth (we can't get out of sync)
- We don't have to ship 30+
.protofiles with the bridge - The .proto files are mostly for static code generation in other languages; for a dynamic client, GetServices is sufficient
What we read from KSP
Per poll, the bridge makes ~280 procedure calls (for a stock KSP save
with 15 bodies and 5 vessels). At BRIDGE_POLL_MS=1000 that's
comfortably within what kRPC can handle on loopback. If you need more
throughput, the obvious optimization is to batch the calls into a
single KRPC.Request with multiple ProcedureCall entries (the
server already supports this; we just don't use it yet).
Top-level
| Procedure | Returns | Used for |
|---|---|---|
SpaceCenter.GetUT() |
double |
Universal Time (game seconds since epoch) |
SpaceCenter.GetBodies() |
list<CelestialBody> |
Object ids of all bodies |
SpaceCenter.GetVessels() |
list<Vessel> |
Object ids of all vessels |
Per CelestialBody (8 calls per body)
| Procedure | Returns | Field |
|---|---|---|
CelestialBody.GetName(self) |
string |
name |
CelestialBody.GetParent(self) |
CelestialBody (nullable) |
parentId |
CelestialBody.GetRadius(self) |
double |
radius (m) |
CelestialBody.GetSphereOfInfluence(self) |
double |
sphereOfInfluence (m) |
CelestialBody.GetGravitationalParameter(self) |
double |
μ (m³/s²) |
CelestialBody.GetRotationPeriod(self) |
double |
rotationPeriod (s) |
CelestialBody.GetAxialTilt(self) |
double |
axialTilt (rad) |
CelestialBody.GetOrbit(self) |
Orbit |
(then 8 orbit calls) |
Per Orbit (8 calls per orbit)
| Procedure | Returns | Field |
|---|---|---|
Orbit.GetSemiMajorAxis(self) |
double |
semiMajorAxis (m) |
Orbit.GetEccentricity(self) |
double |
eccentricity |
Orbit.GetInclination(self) |
double |
inclination (rad) |
Orbit.GetLongitudeOfAscendingNode(self) |
double |
longitudeOfAscendingNode (rad) |
Orbit.GetArgumentOfPeriapsis(self) |
double |
argumentOfPeriapsis (rad) |
Orbit.GetMeanAnomalyAtEpoch(self) |
double |
meanAnomalyAtEpoch (rad) |
Orbit.GetEpoch(self) |
double |
epoch (s) |
Orbit.GetReferenceBody(self) |
CelestialBody (nullable) |
(for verification only) |
Per Vessel (5 calls per vessel)
| Procedure | Returns | Field |
|---|---|---|
Vessel.GetName(self) |
string |
name |
Vessel.GetType(self) |
VesselType (enum) |
type (resolved to name) |
Vessel.GetSituation(self) |
VesselSituation (enum) |
situation (raw int code) |
Vessel.GetOrbit(self) |
Orbit |
(then 8 orbit calls) |
Vessel.GetReferenceBody(self) |
CelestialBody |
referenceBodyId (resolved to name) |
What's NOT in scope yet (deferred work)
- Streams — we don't subscribe to kRPC streams yet. We're
polling. For a real-time UI, switching to streams (or hybrid
poll+stream) would reduce latency and load. kRPC has
AddStreamand the stream port is already wired in. - Batched calls — every kRPC call is its own request. We could
batch multiple
ProcedureCallentries in a singleRequestfor ~10x throughput. - Ground stations — kRPC doesn't expose ground stations natively. The ksp-bridge accepts them as static config or via mod integration.
- Comm nets / signal strength — needs the
CommNetAPI. kRPC has it, but we don't use it yet. - Crew / science — not in the ksp-bridge scope right now.
- Maneuver nodes — easy to add (
Vessel.GetManeuverNode()etc.) but not needed for the live map / mission clock.
Troubleshooting
no kRPC server at 127.0.0.1:50000
Either KSP isn't running, or the kRPC server isn't enabled. Open the kRPC window in-game (Alt+F12) and make sure "Start server" is checked.
procedure not found in service cache
The kRPC server returned a procedure that we don't know about. This usually means the kRPC version is older or newer than we expect (we target 0.5.x). The ServiceCache will log the procedures it knows about; cross-check with the in-game kRPC window.
wrong number of arguments
The procedure signature in the cache doesn't match what we're sending.
This can happen if the kRPC version has a different parameter order
or count for a procedure we use. The fix is in
apps/tools/ksp-bridge/src/extract.ts — adjust the call site.