From 0cbbdd6e4f3547dfd735507896e052de2b1a3ef1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 12:04:07 +0000 Subject: [PATCH 01/13] Initial plan From ebe79edd6bf79ea858c150440a79693a024df894 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 12:11:16 +0000 Subject: [PATCH 02/13] Add MSFS 2024 CommBus (Communication API) support Agent-Logs-Url: https://github.com/EvenAR/node-simconnect/sessions/ceb89d55-c4e6-4aa0-96ba-50037282d643 --- src/SimConnectConnection.ts | 51 +++++++++++++++++++++++++++++++++ src/SimConnectSocket.ts | 2 ++ src/enums/CommBusBroadcastTo.ts | 4 +++ src/enums/index.ts | 1 + src/recv/RecvCommBus.ts | 15 ++++++++++ src/recv/index.ts | 1 + 6 files changed, 74 insertions(+) create mode 100644 src/enums/CommBusBroadcastTo.ts create mode 100644 src/recv/RecvCommBus.ts diff --git a/src/SimConnectConnection.ts b/src/SimConnectConnection.ts index 3bc43b0..7ba6c79 100644 --- a/src/SimConnectConnection.ts +++ b/src/SimConnectConnection.ts @@ -66,6 +66,8 @@ import type { SimConnectMessage } from './SimConnectSocket'; import Timeout = NodeJS.Timeout; import { RecvEnumerateSimobjectAndLiveryList } from './recv/RecvEnumerateSimobjectAndLiveryList'; import { RecvFlowEvent } from './recv/RecvFlowEvent'; +import { RecvCommBus } from './recv/RecvCommBus'; +import { CommBusBroadcastTo } from './enums/CommBusBroadcastTo'; type OpenPacketData = { major: number; @@ -159,6 +161,7 @@ interface SimConnectRecvEvents { recvEnumerateSimobjectAndLiveryList: RecvEnumerateSimobjectAndLiveryList ) => void; flowEvent: (recvFlowEvent: RecvFlowEvent) => void; + commBusEvent: (recvCommBus: RecvCommBus) => void; } type ConnectionOptions = @@ -1846,6 +1849,51 @@ class SimConnectConnection extends EventEmitter { return this._buildAndSend(packet); } + /** + * Used to call a communication (CommBus) event + * + * @param eventName - The name of the CommBus event to call + * @param data - The data payload to send with the event + * @param flags - Determines who the event is broadcast to + * @returns sendId of packet (can be used to identify packet when exception event occurs) + */ + callCommBusEvent(eventName: string, data: Buffer, flags: CommBusBroadcastTo): number { + if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); + + const packet = this._beginPacket(0x5f) + .putString(eventName, SimConnectConstants.MAX_PATH) + .putUint32(flags) + .putUint32(data.length) + .putBytes(data); + return this._buildAndSend(packet); + } + + /** + * Used to subscribe the client to a communication (CommBus) event + * + * @param eventName - The name of the CommBus event to subscribe to + * @returns sendId of packet (can be used to identify packet when exception event occurs) + */ + subscribeToCommBusEvent(eventName: string): number { + if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); + + const packet = this._beginPacket(0x60).putString(eventName, SimConnectConstants.MAX_PATH); + return this._buildAndSend(packet); + } + + /** + * Used to unsubscribe the client from a communication (CommBus) event + * + * @param eventName - The name of the CommBus event to unsubscribe from + * @returns sendId of packet (can be used to identify packet when exception event occurs) + */ + unsubscribeToCommBusEvent(eventName: string): number { + if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); + + const packet = this._beginPacket(0x61).putString(eventName, SimConnectConstants.MAX_PATH); + return this._buildAndSend(packet); + } + close() { if (this._openTimeout !== null) { clearTimeout(this._openTimeout); @@ -2004,6 +2052,9 @@ class SimConnectConnection extends EventEmitter { case RecvID.ID_FLOW_EVENT: this.emit('flowEvent', new RecvFlowEvent(data)); break; + case RecvID.ID_COMM_BUS: + this.emit('commBusEvent', new RecvCommBus(data)); + break; } } diff --git a/src/SimConnectSocket.ts b/src/SimConnectSocket.ts index d93ebde..596cd3c 100644 --- a/src/SimConnectSocket.ts +++ b/src/SimConnectSocket.ts @@ -47,6 +47,8 @@ enum RecvID { ID_ENUMERATE_INPUT_EVENT_PARAMS, ID_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST, ID_FLOW_EVENT, + // MSFS 2024 (SunRise) - CommBus: + ID_COMM_BUS, } interface SimConnectMessage { diff --git a/src/enums/CommBusBroadcastTo.ts b/src/enums/CommBusBroadcastTo.ts new file mode 100644 index 0000000..4e6491a --- /dev/null +++ b/src/enums/CommBusBroadcastTo.ts @@ -0,0 +1,4 @@ +export enum CommBusBroadcastTo { + /** Broadcast the CommBus event to all subscribers */ + ALL = 0, +} diff --git a/src/enums/index.ts b/src/enums/index.ts index eb3242f..2364467 100644 --- a/src/enums/index.ts +++ b/src/enums/index.ts @@ -13,3 +13,4 @@ export * from './InputEventType'; export * from './FacilityDataType'; export * from './FlowEvent'; export * from './JetwayStatus'; +export * from './CommBusBroadcastTo'; diff --git a/src/recv/RecvCommBus.ts b/src/recv/RecvCommBus.ts new file mode 100644 index 0000000..c4fdeae --- /dev/null +++ b/src/recv/RecvCommBus.ts @@ -0,0 +1,15 @@ +import { SimConnectConstants } from '../SimConnectConstants'; +import { RawBuffer } from '../RawBuffer'; + +export class RecvCommBus { + /** The name of the CommBus event */ + eventName: string; + + /** The data payload associated with the CommBus event */ + data: Buffer; + + constructor(data: RawBuffer) { + this.eventName = data.readString(SimConnectConstants.MAX_PATH); + this.data = data.readBytes(data.remaining()); + } +} diff --git a/src/recv/index.ts b/src/recv/index.ts index 0b208fc..195cb45 100644 --- a/src/recv/index.ts +++ b/src/recv/index.ts @@ -33,3 +33,4 @@ export * from './RecvActionCallback'; export * from './RecvListTemplate'; export * from './RecvFlowEvent'; export * from './RecvEnumerateSimobjectAndLiveryList'; +export * from './RecvCommBus'; From 08d69fda3d0a8580f139025c93b26cb60c7c585f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 12:35:19 +0000 Subject: [PATCH 03/13] Update CommBus API: proper enum values, string payload, decoded recv data Agent-Logs-Url: https://github.com/EvenAR/node-simconnect/sessions/c44abc65-3215-4717-8732-d5ab2229b613 --- src/SimConnectConnection.ts | 15 +++++++++++---- src/enums/CommBusBroadcastTo.ts | 12 ++++++++++-- src/recv/RecvCommBus.ts | 6 +++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/SimConnectConnection.ts b/src/SimConnectConnection.ts index 7ba6c79..ec23093 100644 --- a/src/SimConnectConnection.ts +++ b/src/SimConnectConnection.ts @@ -1853,18 +1853,25 @@ class SimConnectConnection extends EventEmitter { * Used to call a communication (CommBus) event * * @param eventName - The name of the CommBus event to call - * @param data - The data payload to send with the event + * @param payload - The data payload to send with the event (string or object serialized to JSON) * @param flags - Determines who the event is broadcast to * @returns sendId of packet (can be used to identify packet when exception event occurs) */ - callCommBusEvent(eventName: string, data: Buffer, flags: CommBusBroadcastTo): number { + callCommBusEvent( + eventName: string, + payload: string | object, + flags: CommBusBroadcastTo + ): number { if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); + const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload); + const payloadBuffer = Buffer.from(payloadString, 'utf8'); + const packet = this._beginPacket(0x5f) .putString(eventName, SimConnectConstants.MAX_PATH) .putUint32(flags) - .putUint32(data.length) - .putBytes(data); + .putUint32(payloadBuffer.length) + .putBytes(payloadBuffer); return this._buildAndSend(packet); } diff --git a/src/enums/CommBusBroadcastTo.ts b/src/enums/CommBusBroadcastTo.ts index 4e6491a..7b65e01 100644 --- a/src/enums/CommBusBroadcastTo.ts +++ b/src/enums/CommBusBroadcastTo.ts @@ -1,4 +1,12 @@ export enum CommBusBroadcastTo { - /** Broadcast the CommBus event to all subscribers */ - ALL = 0, + JS = 1 << 0, + WASM = 1 << 1, + SIMCONNECT = 1 << 3, + SIMCONNECT_SELF_CALL = 1 << 4, + DEFAULT = CommBusBroadcastTo.JS | CommBusBroadcastTo.WASM | CommBusBroadcastTo.SIMCONNECT, + ALL_SIMCONNECT = CommBusBroadcastTo.SIMCONNECT | CommBusBroadcastTo.SIMCONNECT_SELF_CALL, + ALL = CommBusBroadcastTo.JS | + CommBusBroadcastTo.WASM | + CommBusBroadcastTo.SIMCONNECT | + CommBusBroadcastTo.SIMCONNECT_SELF_CALL, } diff --git a/src/recv/RecvCommBus.ts b/src/recv/RecvCommBus.ts index c4fdeae..06a0232 100644 --- a/src/recv/RecvCommBus.ts +++ b/src/recv/RecvCommBus.ts @@ -5,11 +5,11 @@ export class RecvCommBus { /** The name of the CommBus event */ eventName: string; - /** The data payload associated with the CommBus event */ - data: Buffer; + /** The data payload associated with the CommBus event, decoded as a string */ + data: string; constructor(data: RawBuffer) { this.eventName = data.readString(SimConnectConstants.MAX_PATH); - this.data = data.readBytes(data.remaining()); + this.data = data.readBytes(data.remaining()).toString('utf8'); } } From 0b45f98f51852039de7e6c79a778f535808e4761 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 12:38:08 +0000 Subject: [PATCH 04/13] Use latin1 encoding in CommBus (consistent with rest of codebase) Agent-Logs-Url: https://github.com/EvenAR/node-simconnect/sessions/ed323ee1-14b9-417f-8f51-b6adb875afd6 --- src/SimConnectConnection.ts | 2 +- src/recv/RecvCommBus.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SimConnectConnection.ts b/src/SimConnectConnection.ts index ec23093..a34f1dc 100644 --- a/src/SimConnectConnection.ts +++ b/src/SimConnectConnection.ts @@ -1865,7 +1865,7 @@ class SimConnectConnection extends EventEmitter { if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload); - const payloadBuffer = Buffer.from(payloadString, 'utf8'); + const payloadBuffer = Buffer.from(payloadString, 'latin1'); const packet = this._beginPacket(0x5f) .putString(eventName, SimConnectConstants.MAX_PATH) diff --git a/src/recv/RecvCommBus.ts b/src/recv/RecvCommBus.ts index 06a0232..78eceef 100644 --- a/src/recv/RecvCommBus.ts +++ b/src/recv/RecvCommBus.ts @@ -10,6 +10,6 @@ export class RecvCommBus { constructor(data: RawBuffer) { this.eventName = data.readString(SimConnectConstants.MAX_PATH); - this.data = data.readBytes(data.remaining()).toString('utf8'); + this.data = data.readBytes(data.remaining()).toString('latin1'); } } From 3b1475e66cd34b46710b97de997f239c248a4f03 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 12:38:59 +0000 Subject: [PATCH 05/13] Add .github/copilot-instructions.md with codebase conventions Agent-Logs-Url: https://github.com/EvenAR/node-simconnect/sessions/ed323ee1-14b9-417f-8f51-b6adb875afd6 --- .github/copilot-instructions.md | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..e1ef0da --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,71 @@ +# Copilot Instructions + +## Project overview + +`node-simconnect` is a TypeScript/Node.js library that implements the SimConnect binary protocol used by Microsoft Flight Simulator (FSX through MSFS 2024). It communicates with the simulator over a TCP socket using little-endian binary packets. + +## Repository structure + +``` +src/ + SimConnectConnection.ts # Main class – all public API methods live here + SimConnectSocket.ts # Low-level socket + packet framing; houses RecvID enum + SimConnectPacketBuilder.ts# Fluent builder for outbound packets + RawBuffer.ts # Binary read/write helper used by all Recv* classes + SimConnectConstants.ts # Shared constants (e.g. MAX_PATH = 260) + enums/ # One file per TypeScript enum, exported via index.ts + recv/ # One file per inbound packet type, exported via index.ts + flags/ # Bit-flag enums + datastructures/ # Composite data types + dto/ # Data-transfer objects +tests/ # Jest test suite +``` + +## Adding a new API method + +1. **Recv struct** – if the server sends a response packet, add `src/recv/RecvXxx.ts` and export it from `src/recv/index.ts`. +2. **RecvID** – add the new `ID_XXX` value to the `RecvID` enum in `SimConnectSocket.ts` (values follow the packet-ID order in the SimConnect header). +3. **Enum** – if the method needs a new enum, add `src/enums/XxxEnum.ts` and export it from `src/enums/index.ts`. Mirror the exact C++ enum values from the SDK docs (don't invent values). +4. **Method** – add the public method to `SimConnectConnection.ts`. Guard with `Protocol.XxxVersion` where required: + ```ts + if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); + ``` +5. **Event** – register the new `RecvID` case in the `_handleMessage` switch in `SimConnectConnection.ts` and emit the event on `this`. + +## Encoding conventions + +- **All strings** (both reading and writing) use **`latin1`** encoding — the same encoding used by `RawBuffer.readString()` / `writeString()` and the helpers in `RawBuffer.ts`. Never use `utf8`. +- Fixed-length string fields are written/read with `SimConnectConstants.MAX_PATH` (260 bytes). +- Numbers are little-endian (`readUInt32LE`, `writeUInt32LE`, etc.) — this is handled by `RawBuffer` helpers. + +## Enum conventions + +- Enum file names: `PascalCase.ts` (e.g. `CommBusBroadcastTo.ts`). +- Enum member names: use short `PascalCase` names that strip the repetitive C++ prefix (e.g. `SIMCONNECT_COMM_BUS_BROADCAST_TO_JS` → `JS`). +- Values must exactly match the SDK C++ definitions — use bit-shift literals (`1 << 0`) for flag enums and composite values for combined flags. + +## Recv class conventions + +- Constructor takes a single `RawBuffer` argument. +- Read fields in the exact order they appear in the C++ struct. +- Fixed-length string fields: `data.readString(SimConnectConstants.MAX_PATH)`. +- Variable-length byte payloads that represent text: `data.readBytes(data.remaining()).toString('latin1')`. +- Typed as `string` (not `Buffer`) when the payload is known to be text. + +## callCommBusEvent / payload helpers + +- Methods that send a text payload accept `string | object`. Objects are serialized with `JSON.stringify` before encoding. +- Encode the resulting string with `Buffer.from(str, 'latin1')` before writing. + +## Build & test + +```bash +npm install # install dependencies +npm run build # compile TypeScript (tsc) +npm test # run Jest tests +npm run lint # ESLint + Prettier (via lint-staged on commit) +``` + +- `tsconfig.build.json` is used for the production build (outputs to `dist/`). +- Tests live in `tests/` and use Jest. +- Pre-commit hook runs `lint-staged` (ESLint fix → Prettier → `tsc --noEmit`). From 6921ad1f50bb2dda39e4225bb4a0d36996a07121 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 12:40:14 +0000 Subject: [PATCH 06/13] Replace copilot instructions with focused instructions + add-api-method skill Agent-Logs-Url: https://github.com/EvenAR/node-simconnect/sessions/ed323ee1-14b9-417f-8f51-b6adb875afd6 --- .github/copilot-instructions.md | 72 +++++-------- .github/prompts/add-api-method.prompt.md | 132 +++++++++++++++++++++++ 2 files changed, 158 insertions(+), 46 deletions(-) create mode 100644 .github/prompts/add-api-method.prompt.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e1ef0da..d77ba72 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -8,64 +8,44 @@ ``` src/ - SimConnectConnection.ts # Main class – all public API methods live here - SimConnectSocket.ts # Low-level socket + packet framing; houses RecvID enum - SimConnectPacketBuilder.ts# Fluent builder for outbound packets - RawBuffer.ts # Binary read/write helper used by all Recv* classes - SimConnectConstants.ts # Shared constants (e.g. MAX_PATH = 260) - enums/ # One file per TypeScript enum, exported via index.ts - recv/ # One file per inbound packet type, exported via index.ts - flags/ # Bit-flag enums - datastructures/ # Composite data types - dto/ # Data-transfer objects -tests/ # Jest test suite + SimConnectConnection.ts # Main class – all public API methods live here + SimConnectSocket.ts # Low-level socket + packet framing; houses RecvID enum + SimConnectPacketBuilder.ts # Fluent builder for outbound packets + RawBuffer.ts # Binary read/write helper used by all Recv* classes + SimConnectConstants.ts # Shared constants (e.g. MAX_PATH = 260) + enums/ # One file per TypeScript enum, exported via index.ts + recv/ # One file per inbound packet type, exported via index.ts + flags/ # Bit-flag enums + datastructures/ # Composite data types + dto/ # Data-transfer objects +tests/ # Jest test suite ``` -## Adding a new API method - -1. **Recv struct** – if the server sends a response packet, add `src/recv/RecvXxx.ts` and export it from `src/recv/index.ts`. -2. **RecvID** – add the new `ID_XXX` value to the `RecvID` enum in `SimConnectSocket.ts` (values follow the packet-ID order in the SimConnect header). -3. **Enum** – if the method needs a new enum, add `src/enums/XxxEnum.ts` and export it from `src/enums/index.ts`. Mirror the exact C++ enum values from the SDK docs (don't invent values). -4. **Method** – add the public method to `SimConnectConnection.ts`. Guard with `Protocol.XxxVersion` where required: - ```ts - if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); - ``` -5. **Event** – register the new `RecvID` case in the `_handleMessage` switch in `SimConnectConnection.ts` and emit the event on `this`. - ## Encoding conventions -- **All strings** (both reading and writing) use **`latin1`** encoding — the same encoding used by `RawBuffer.readString()` / `writeString()` and the helpers in `RawBuffer.ts`. Never use `utf8`. +- **All strings** (both reading and writing) use **`latin1`** encoding — the same encoding used by `RawBuffer.readString()` / `writeString()`. Never use `utf8`. - Fixed-length string fields are written/read with `SimConnectConstants.MAX_PATH` (260 bytes). -- Numbers are little-endian (`readUInt32LE`, `writeUInt32LE`, etc.) — this is handled by `RawBuffer` helpers. +- Numbers are little-endian — handled automatically by `RawBuffer` helpers. +- Variable-length text payloads sent as bytes: `Buffer.from(str, 'latin1')`. +- Variable-length text payloads received as bytes: `data.readBytes(n).toString('latin1')`. ## Enum conventions -- Enum file names: `PascalCase.ts` (e.g. `CommBusBroadcastTo.ts`). -- Enum member names: use short `PascalCase` names that strip the repetitive C++ prefix (e.g. `SIMCONNECT_COMM_BUS_BROADCAST_TO_JS` → `JS`). -- Values must exactly match the SDK C++ definitions — use bit-shift literals (`1 << 0`) for flag enums and composite values for combined flags. - -## Recv class conventions - -- Constructor takes a single `RawBuffer` argument. -- Read fields in the exact order they appear in the C++ struct. -- Fixed-length string fields: `data.readString(SimConnectConstants.MAX_PATH)`. -- Variable-length byte payloads that represent text: `data.readBytes(data.remaining()).toString('latin1')`. -- Typed as `string` (not `Buffer`) when the payload is known to be text. - -## callCommBusEvent / payload helpers - -- Methods that send a text payload accept `string | object`. Objects are serialized with `JSON.stringify` before encoding. -- Encode the resulting string with `Buffer.from(str, 'latin1')` before writing. +- File names: `PascalCase.ts` (e.g. `CommBusBroadcastTo.ts`), exported from `src/enums/index.ts`. +- Member names: short `PascalCase` stripping the repetitive C++ prefix (e.g. `SIMCONNECT_COMM_BUS_BROADCAST_TO_JS` → `JS`). +- Values must exactly match the SDK C++ definitions — use bit-shift literals (`1 << 0`) for flags and composite expressions for combined values. ## Build & test ```bash -npm install # install dependencies -npm run build # compile TypeScript (tsc) -npm test # run Jest tests -npm run lint # ESLint + Prettier (via lint-staged on commit) +npm install # install dependencies +npm run build # compile TypeScript (tsc -p tsconfig.build.json → dist/) +npm test # run Jest tests +npm run lint # ESLint + Prettier ``` -- `tsconfig.build.json` is used for the production build (outputs to `dist/`). -- Tests live in `tests/` and use Jest. - Pre-commit hook runs `lint-staged` (ESLint fix → Prettier → `tsc --noEmit`). + +## Skills / prompt files + +See [`.github/prompts/`](.github/prompts/) for reusable Copilot prompt files (skills). diff --git a/.github/prompts/add-api-method.prompt.md b/.github/prompts/add-api-method.prompt.md new file mode 100644 index 0000000..8a3eeb0 --- /dev/null +++ b/.github/prompts/add-api-method.prompt.md @@ -0,0 +1,132 @@ +--- +mode: agent +description: Add a new SimConnect API method to node-simconnect +tools: + - read_file + - create_file + - insert_edit_into_file + - run_in_terminal +--- + +Add a new SimConnect API method following the steps below. Ask for the method name, packet IDs, and SDK struct/enum definitions before starting if they are not already provided. + +## Checklist + +### 1. Recv struct (only if the server sends a response packet) + +Create `src/recv/RecvXxx.ts`: + +```ts +import { SimConnectConstants } from '../SimConnectConstants'; +import { RawBuffer } from '../RawBuffer'; + +export class RecvXxx { + // one property per field in the C++ struct + myField: number; + + constructor(data: RawBuffer) { + // read fields in the exact order they appear in the C++ struct + this.myField = data.readUint32(); + } +} +``` + +Rules: + +- Constructor takes a single `RawBuffer` argument. +- Read fields in the exact order they appear in the C++ struct. +- Fixed-length string fields: `data.readString(SimConnectConstants.MAX_PATH)`. +- Variable-length text payloads: `data.readBytes(data.remaining()).toString('latin1')` typed as `string`. +- Raw byte blobs that are not text: `data.readBytes(n)` typed as `Buffer`. + +Then export the new class from `src/recv/index.ts`: + +```ts +export { RecvXxx } from './RecvXxx'; +``` + +### 2. RecvID (only if step 1 applies) + +Add the new value to the `RecvID` enum in `src/SimConnectSocket.ts`. Values must follow the packet-ID order from the SimConnect header — do not guess; use the provided SDK information. + +```ts +ID_XXX = , +``` + +### 3. New enum (only if the method needs one) + +Create `src/enums/XxxEnum.ts`, mirroring the exact C++ values from the SDK docs: + +```ts +export enum XxxEnum { + VALUE_A = 1 << 0, + VALUE_B = 1 << 1, + COMBINED = XxxEnum.VALUE_A | XxxEnum.VALUE_B, +} +``` + +Rules: + +- File name: `PascalCase.ts`. +- Member names: short `PascalCase` stripping the repetitive C++ prefix. +- Values must exactly match the SDK definitions. + +Then export from `src/enums/index.ts`: + +```ts +export { XxxEnum } from './XxxEnum'; +``` + +### 4. Method on SimConnectConnection + +Add the public method to `src/SimConnectConnection.ts`. + +```ts +/** + * JSDoc describing the method. + * + * @param foo - description + * @returns sendId of packet + */ +methodName(foo: string): number { + // guard for version-gated methods: + if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); + + const packet = this._beginPacket(0xNN) + .putString(foo, SimConnectConstants.MAX_PATH) + .putUint32(someFlag); + return this._buildAndSend(packet); +} +``` + +Payload encoding rules: + +- All strings use **`latin1`**. Never use `utf8`. +- Methods that send user-supplied text accept `string | object`; objects are serialized with `JSON.stringify` first, then encoded with `Buffer.from(str, 'latin1')`. +- Fixed-length strings: `.putString(value, SimConnectConstants.MAX_PATH)`. +- Variable-length bytes: `.putUint32(buf.length).putBytes(buf)`. + +### 5. Event handler (only if step 1 applies) + +In the `_handleMessage` switch in `src/SimConnectConnection.ts`, add a case for the new `RecvID` and emit the event: + +```ts +case RecvID.ID_XXX: { + const recv = new RecvXxx(packet); + this.emit('xxxEvent', recv); + break; +} +``` + +Also add the event to the `SimConnectRecvEvents` interface at the top of the file: + +```ts +xxxEvent: (recv: RecvXxx) => void; +``` + +### 6. Validate + +```bash +npm run build # must produce no TypeScript errors +npm test # all tests must pass +``` From dc59596388e7a58b973cbd707a4dd48251842867 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 12:52:49 +0000 Subject: [PATCH 07/13] Sync RecvID enum with MSFS 2024 values; update skill with SDK lookup instruction Agent-Logs-Url: https://github.com/EvenAR/node-simconnect/sessions/0a7d7ece-a43c-4884-bac7-7093d06a5fa7 --- .github/prompts/add-api-method.prompt.md | 8 ++++++-- src/SimConnectSocket.ts | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/prompts/add-api-method.prompt.md b/.github/prompts/add-api-method.prompt.md index 8a3eeb0..9acf00a 100644 --- a/.github/prompts/add-api-method.prompt.md +++ b/.github/prompts/add-api-method.prompt.md @@ -47,10 +47,14 @@ export { RecvXxx } from './RecvXxx'; ### 2. RecvID (only if step 1 applies) -Add the new value to the `RecvID` enum in `src/SimConnectSocket.ts`. Values must follow the packet-ID order from the SimConnect header — do not guess; use the provided SDK information. +**Always look up the official SDK enum before adding a new value.** +The authoritative reference is the MSFS 2024 SDK documentation: + + +Add the new value to the `RecvID` enum in `src/SimConnectSocket.ts` at the exact position it occupies in the SDK `SIMCONNECT_RECV_ID` enum — do **not** guess or append blindly to the end. ```ts -ID_XXX = , +ID_XXX, // position must match SDK enum order ``` ### 3. New enum (only if the method needs one) diff --git a/src/SimConnectSocket.ts b/src/SimConnectSocket.ts index 596cd3c..d9d4706 100644 --- a/src/SimConnectSocket.ts +++ b/src/SimConnectSocket.ts @@ -47,7 +47,11 @@ enum RecvID { ID_ENUMERATE_INPUT_EVENT_PARAMS, ID_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST, ID_FLOW_EVENT, - // MSFS 2024 (SunRise) - CommBus: + // MSFS 2024 (SunRise): + ID_CAMERA_DATA, + ID_CAMERA_DEFINITION_LIST, + ID_CAMERA_STATUS, + ID_CAMERA_WORLD_LOCKER, ID_COMM_BUS, } From 1d4d2a0290d4db93e0e170177a19e24ffa5b077b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 12:53:37 +0000 Subject: [PATCH 08/13] Add explicit numeric values to MSFS 2024 RecvID enum members Agent-Logs-Url: https://github.com/EvenAR/node-simconnect/sessions/0a7d7ece-a43c-4884-bac7-7093d06a5fa7 --- src/SimConnectSocket.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/SimConnectSocket.ts b/src/SimConnectSocket.ts index d9d4706..c8ac143 100644 --- a/src/SimConnectSocket.ts +++ b/src/SimConnectSocket.ts @@ -48,11 +48,11 @@ enum RecvID { ID_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST, ID_FLOW_EVENT, // MSFS 2024 (SunRise): - ID_CAMERA_DATA, - ID_CAMERA_DEFINITION_LIST, - ID_CAMERA_STATUS, - ID_CAMERA_WORLD_LOCKER, - ID_COMM_BUS, + ID_CAMERA_DATA = 40, + ID_CAMERA_DEFINITION_LIST = 41, + ID_CAMERA_STATUS = 42, + ID_CAMERA_WORLD_LOCKER = 43, + ID_COMM_BUS = 44, } interface SimConnectMessage { From 108703cda23eaa6440ef2fbcfd25c62f01e91b93 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 14:07:04 +0000 Subject: [PATCH 09/13] fix: correct add-api-method skill for string lengths and object support Agent-Logs-Url: https://github.com/EvenAR/node-simconnect/sessions/39d06b82-ff96-4604-b838-9d634a1fe54c Co-authored-by: EvenAR <5177099+EvenAR@users.noreply.github.com> --- .github/prompts/add-api-method.prompt.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/prompts/add-api-method.prompt.md b/.github/prompts/add-api-method.prompt.md index 9acf00a..57b6b2c 100644 --- a/.github/prompts/add-api-method.prompt.md +++ b/.github/prompts/add-api-method.prompt.md @@ -35,7 +35,7 @@ Rules: - Constructor takes a single `RawBuffer` argument. - Read fields in the exact order they appear in the C++ struct. -- Fixed-length string fields: `data.readString(SimConnectConstants.MAX_PATH)`. +- Fixed-length string fields: `data.readString(N)` where **N is the exact byte length documented for that field in the SDK struct** — do not default to `SimConnectConstants.MAX_PATH` unless the SDK explicitly uses `MAX_PATH` for that field. - Variable-length text payloads: `data.readBytes(data.remaining()).toString('latin1')` typed as `string`. - Raw byte blobs that are not text: `data.readBytes(n)` typed as `Buffer`. @@ -97,7 +97,7 @@ methodName(foo: string): number { if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); const packet = this._beginPacket(0xNN) - .putString(foo, SimConnectConstants.MAX_PATH) + .putString(foo, N) // N = exact byte length from SDK docs .putUint32(someFlag); return this._buildAndSend(packet); } @@ -106,8 +106,8 @@ methodName(foo: string): number { Payload encoding rules: - All strings use **`latin1`**. Never use `utf8`. -- Methods that send user-supplied text accept `string | object`; objects are serialized with `JSON.stringify` first, then encoded with `Buffer.from(str, 'latin1')`. -- Fixed-length strings: `.putString(value, SimConnectConstants.MAX_PATH)`. +- Fixed-length strings: `.putString(value, N)` where **N is the exact byte length documented for that field in the SDK** — look it up in the official docs, do not default to `SimConnectConstants.MAX_PATH` unless the SDK says so. +- If the SDK documentation explicitly states the string field carries **JSON**, accept `string | object`; objects are serialized with `JSON.stringify` before encoding. Otherwise the parameter type is plain `string`. - Variable-length bytes: `.putUint32(buf.length).putBytes(buf)`. ### 5. Event handler (only if step 1 applies) From 58043b379cb3f8a7cbf14bcc8d9011393bf173f8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 14:07:37 +0000 Subject: [PATCH 10/13] docs: remove repository structure section from copilot instructions Agent-Logs-Url: https://github.com/EvenAR/node-simconnect/sessions/39d06b82-ff96-4604-b838-9d634a1fe54c Co-authored-by: EvenAR <5177099+EvenAR@users.noreply.github.com> --- .github/copilot-instructions.md | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index d77ba72..fe0a2ad 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -4,27 +4,10 @@ `node-simconnect` is a TypeScript/Node.js library that implements the SimConnect binary protocol used by Microsoft Flight Simulator (FSX through MSFS 2024). It communicates with the simulator over a TCP socket using little-endian binary packets. -## Repository structure - -``` -src/ - SimConnectConnection.ts # Main class – all public API methods live here - SimConnectSocket.ts # Low-level socket + packet framing; houses RecvID enum - SimConnectPacketBuilder.ts # Fluent builder for outbound packets - RawBuffer.ts # Binary read/write helper used by all Recv* classes - SimConnectConstants.ts # Shared constants (e.g. MAX_PATH = 260) - enums/ # One file per TypeScript enum, exported via index.ts - recv/ # One file per inbound packet type, exported via index.ts - flags/ # Bit-flag enums - datastructures/ # Composite data types - dto/ # Data-transfer objects -tests/ # Jest test suite -``` - ## Encoding conventions - **All strings** (both reading and writing) use **`latin1`** encoding — the same encoding used by `RawBuffer.readString()` / `writeString()`. Never use `utf8`. -- Fixed-length string fields are written/read with `SimConnectConstants.MAX_PATH` (260 bytes). +- Fixed-length string field lengths vary per field — always check the SDK documentation for the exact size. - Numbers are little-endian — handled automatically by `RawBuffer` helpers. - Variable-length text payloads sent as bytes: `Buffer.from(str, 'latin1')`. - Variable-length text payloads received as bytes: `data.readBytes(n).toString('latin1')`. From 916d2c0c982e649d47581f3784ea690613a103f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 14:10:18 +0000 Subject: [PATCH 11/13] docs: add read-the-docs-first instruction to add-api-method skill Agent-Logs-Url: https://github.com/EvenAR/node-simconnect/sessions/0cd5aadf-c2f3-4c15-b23e-29a82bbdce9b Co-authored-by: EvenAR <5177099+EvenAR@users.noreply.github.com> --- .github/prompts/add-api-method.prompt.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/prompts/add-api-method.prompt.md b/.github/prompts/add-api-method.prompt.md index 57b6b2c..292560d 100644 --- a/.github/prompts/add-api-method.prompt.md +++ b/.github/prompts/add-api-method.prompt.md @@ -10,6 +10,8 @@ tools: Add a new SimConnect API method following the steps below. Ask for the method name, packet IDs, and SDK struct/enum definitions before starting if they are not already provided. +**Before writing any code, look up the method in the official MSFS SDK documentation** to understand the exact function signature, parameter types, string field lengths, and any notes about JSON payloads. The authoritative reference is: + ## Checklist ### 1. Recv struct (only if the server sends a response packet) From 9cbb33c196883074ee3adaf3d02dbd5fd49cf78f Mon Sep 17 00:00:00 2001 From: Even Rognlien Date: Mon, 4 May 2026 22:00:18 +0200 Subject: [PATCH 12/13] fix: correct mistakes in the commbus-APIs implementation --- .github/prompts/add-api-method.prompt.md | 59 ++++++++++++++-------- samples/typescript/commbus.ts | 57 +++++++++++++++++++++ samples/typescript/enumerateSimobjets.ts | 0 src/SimConnectConnection.ts | 63 +++++++++++++++--------- src/SimConnectSocket.ts | 11 ++--- src/recv/RecvCommBus.ts | 13 +++-- 6 files changed, 145 insertions(+), 58 deletions(-) create mode 100644 samples/typescript/commbus.ts create mode 100644 samples/typescript/enumerateSimobjets.ts diff --git a/.github/prompts/add-api-method.prompt.md b/.github/prompts/add-api-method.prompt.md index 292560d..63187d4 100644 --- a/.github/prompts/add-api-method.prompt.md +++ b/.github/prompts/add-api-method.prompt.md @@ -8,9 +8,15 @@ tools: - run_in_terminal --- -Add a new SimConnect API method following the steps below. Ask for the method name, packet IDs, and SDK struct/enum definitions before starting if they are not already provided. +Add a new SimConnect API method following the steps below. -**Before writing any code, look up the method in the official MSFS SDK documentation** to understand the exact function signature, parameter types, string field lengths, and any notes about JSON payloads. The authoritative reference is: +**Before starting, ask the user to provide:** + +1. The method name(s) to implement. +2. The contents of (or path to) their local `SimConnect.h` — this is the single authoritative source for function signatures, packet opcodes, struct field layouts, enum orderings, and string field sizes. Do **not** rely solely on the online docs, which may be outdated or incomplete. + +The online SDK docs are a useful supplement but must never override what `SimConnect.h` says: + ## Checklist @@ -19,7 +25,6 @@ Add a new SimConnect API method following the steps below. Ask for the method na Create `src/recv/RecvXxx.ts`: ```ts -import { SimConnectConstants } from '../SimConnectConstants'; import { RawBuffer } from '../RawBuffer'; export class RecvXxx { @@ -36,10 +41,12 @@ export class RecvXxx { Rules: - Constructor takes a single `RawBuffer` argument. +- **Check if the SDK struct inherits from a base struct** (e.g., `SIMCONNECT_RECV_LIST_TEMPLATE`). If it does, extend the corresponding TypeScript base class (e.g., `RecvListTemplate`) and call `super(data)` first. - Read fields in the exact order they appear in the C++ struct. -- Fixed-length string fields: `data.readString(N)` where **N is the exact byte length documented for that field in the SDK struct** — do not default to `SimConnectConstants.MAX_PATH` unless the SDK explicitly uses `MAX_PATH` for that field. -- Variable-length text payloads: `data.readBytes(data.remaining()).toString('latin1')` typed as `string`. -- Raw byte blobs that are not text: `data.readBytes(n)` typed as `Buffer`. +- Fixed-length string fields: `data.readString(N)` where **N is the exact byte length from the SDK struct**. +- Variable-length text payloads declared with the `SIMCONNECT_STRINGV(name)` macro (expands to `char name[1]`): use `data.readStringV()` typed as `string`. +- Variable-length raw byte blobs that are not text: `data.readBytes(data.remaining())` typed as `Buffer`. +- **Verify every field name and type against the SDK struct** — do not rename fields or change types based on assumptions. Then export the new class from `src/recv/index.ts`: @@ -49,19 +56,20 @@ export { RecvXxx } from './RecvXxx'; ### 2. RecvID (only if step 1 applies) -**Always look up the official SDK enum before adding a new value.** -The authoritative reference is the MSFS 2024 SDK documentation: - +Read the `SIMCONNECT_RECV_ID` enum directly from `SimConnect.h` and add the new value at the exact position it occupies there. Do **not** guess or append blindly to the end. + +**Critical rules:** -Add the new value to the `RecvID` enum in `src/SimConnectSocket.ts` at the exact position it occupies in the SDK `SIMCONNECT_RECV_ID` enum — do **not** guess or append blindly to the end. +- Never assign explicit integer values to enum members (e.g. `ID_FOO = 40`) — rely on TypeScript's sequential auto-increment. Explicit values cause the entire subsequent sequence to shift if any entry is inserted before it. +- Before adding entries, find the full `SIMCONNECT_RECV_ID` enum in `SimConnect.h` and verify the complete ordering. Pay attention to entries that may have been inserted between existing ones. ```ts -ID_XXX, // position must match SDK enum order +ID_XXX, // position must match SDK enum order, no explicit value ``` ### 3. New enum (only if the method needs one) -Create `src/enums/XxxEnum.ts`, mirroring the exact C++ values from the SDK docs: +Create `src/enums/XxxEnum.ts`, mirroring the exact C++ values from `SimConnect.h`: ```ts export enum XxxEnum { @@ -87,19 +95,27 @@ export { XxxEnum } from './XxxEnum'; Add the public method to `src/SimConnectConnection.ts`. +**Before writing the method, determine:** + +- The exact packet opcode (hex ID) — **do not guess**. Opcodes are sequential based on the order functions appear in `SimConnect.h`. To find the correct opcode: + 1. Find the last implemented method in `src/SimConnectConnection.ts` and note its opcode. + 2. Locate that same function in `SimConnect.h` and count forward to the target function. + 3. Increment the last known opcode by the number of steps between them. +- The exact parameter order as declared in the SDK function signature in `SimConnect.h` +- The exact string field sizes + ```ts /** - * JSDoc describing the method. - * - * @param foo - description - * @returns sendId of packet + * @returns sendId of packet (can be used to identify packet when exception event occurs) */ +// Only add @param / summary JSDoc if the description can be copied directly from the +// online SDK docs for the C function. Do NOT guess or paraphrase. methodName(foo: string): number { // guard for version-gated methods: if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); - const packet = this._beginPacket(0xNN) - .putString(foo, N) // N = exact byte length from SDK docs + const packet = this._beginPacket(0xNN) // opcode = last known opcode + offset in SimConnect.h + .putString256(foo) .putUint32(someFlag); return this._buildAndSend(packet); } @@ -108,9 +124,10 @@ methodName(foo: string): number { Payload encoding rules: - All strings use **`latin1`**. Never use `utf8`. -- Fixed-length strings: `.putString(value, N)` where **N is the exact byte length documented for that field in the SDK** — look it up in the official docs, do not default to `SimConnectConstants.MAX_PATH` unless the SDK says so. -- If the SDK documentation explicitly states the string field carries **JSON**, accept `string | object`; objects are serialized with `JSON.stringify` before encoding. Otherwise the parameter type is plain `string`. -- Variable-length bytes: `.putUint32(buf.length).putBytes(buf)`. +- **String field sizes**: use the correct `putStringN` helper matching the byte length in `SimConnect.h` (e.g. `.putString256(value)` for 256-byte fields). Always check the struct definition. +- If `SimConnect.h` or the function's documentation comment explicitly states the payload carries **JSON**, accept `string | object`; objects are serialized with `JSON.stringify` before encoding. Otherwise the parameter type is plain `string`. +- Variable-length string fields declared with `SIMCONNECT_STRINGV` in the SDK struct: `.putUint32(str.length).putString(str)` (no fixed-size argument to `putString`). +- Variable-length byte blobs: `.putUint32(buf.length).putBytes(buf)`. ### 5. Event handler (only if step 1 applies) diff --git a/samples/typescript/commbus.ts b/samples/typescript/commbus.ts new file mode 100644 index 0000000..e6bec35 --- /dev/null +++ b/samples/typescript/commbus.ts @@ -0,0 +1,57 @@ +import { CommBusBroadcastTo, open, Protocol } from '../../dist'; + +const MY_COMM_BUS_EVENT_ID = 1234; + +open('CommBus sample', Protocol.SunRise) + .then(({ recvOpen, handle }) => { + console.log('Connected: ', recvOpen); + + handle.subscribeToCommBusEvent(MY_COMM_BUS_EVENT_ID, 'MyCommBusEvent'); + + setInterval(() => { + const payload = JSON.stringify({ + message: 'Hello from Node.js!', + timestamp: new Date(), + }); + handle.callCommBusEvent('MyCommBusEvent', CommBusBroadcastTo.ALL_SIMCONNECT, payload); + }, 1000); + + setTimeout(() => { + console.log('Unsubscribing from CommBus event'); + handle.unsubscribeToCommBusEvent(MY_COMM_BUS_EVENT_ID); + }, 5000); + + function handleReceivedData(text: string) { + const json = JSON.parse(text); + console.log('Received commbus event data:', json); + } + + let receptionBuffer = ''; // In case the data is split across multiple events, we buffer it until we have the full message + handle.on('commBusEvent', recvCommBusEvent => { + switch (recvCommBusEvent.eventId) { + case MY_COMM_BUS_EVENT_ID: + if (recvCommBusEvent.outOf === 1) { + handleReceivedData(recvCommBusEvent.data); + } else { + receptionBuffer += recvCommBusEvent.data; + if (recvCommBusEvent.entryNumber + 1 === recvCommBusEvent.outOf) { + handleReceivedData(receptionBuffer); + receptionBuffer = ''; + } + } + + break; + } + }); + + handle.on('error', error => { + console.log('Error:', error); + }); + + handle.on('exception', recvException => { + console.log('SimConnect Exception:', recvException); + }); + }) + .catch(error => { + console.log('Failed to connect', error); + }); diff --git a/samples/typescript/enumerateSimobjets.ts b/samples/typescript/enumerateSimobjets.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/SimConnectConnection.ts b/src/SimConnectConnection.ts index a34f1dc..ccc3b74 100644 --- a/src/SimConnectConnection.ts +++ b/src/SimConnectConnection.ts @@ -1850,54 +1850,57 @@ class SimConnectConnection extends EventEmitter { } /** - * Used to call a communication (CommBus) event + * TODO: implement new camera APIs here + * + * SimConnect_CameraAcquire: 0x5f + * SimConnect_CameraRelease: 0x60 + * SimConnect_CameraGetStatus: 0x61 + * SimConnect_CameraSet: 0x62 + * SimConnect_CameraGet: 0x63 + * SimConnect_CameraEnableFlag: 0x64 + * SimConnect_CameraDisableFlag: 0x65 + * SimConnect_SubscribeToCameraStatusUpdate: 0x66 + * SimConnect_UnsubscribeToCameraStatusUpdate: 0x67 + * SimConnect_EnumerateCameraDefinitions: 0x68 + * SimConnect_CameraSetUsingCameraDefinition: 0x69 + */ + + /** * - * @param eventName - The name of the CommBus event to call - * @param payload - The data payload to send with the event (string or object serialized to JSON) - * @param flags - Determines who the event is broadcast to * @returns sendId of packet (can be used to identify packet when exception event occurs) */ - callCommBusEvent( - eventName: string, - payload: string | object, - flags: CommBusBroadcastTo - ): number { + subscribeToCommBusEvent(eventId: number, eventName: string): number { if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); - const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload); - const payloadBuffer = Buffer.from(payloadString, 'latin1'); + const packet = this._beginPacket(0x6a).putUint32(eventId).putString256(eventName); - const packet = this._beginPacket(0x5f) - .putString(eventName, SimConnectConstants.MAX_PATH) - .putUint32(flags) - .putUint32(payloadBuffer.length) - .putBytes(payloadBuffer); return this._buildAndSend(packet); } /** - * Used to subscribe the client to a communication (CommBus) event * - * @param eventName - The name of the CommBus event to subscribe to * @returns sendId of packet (can be used to identify packet when exception event occurs) */ - subscribeToCommBusEvent(eventName: string): number { + unsubscribeToCommBusEvent(eventId: number): number { if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); - const packet = this._beginPacket(0x60).putString(eventName, SimConnectConstants.MAX_PATH); + const packet = this._beginPacket(0x6b).putUint32(eventId); + return this._buildAndSend(packet); } /** - * Used to unsubscribe the client from a communication (CommBus) event * - * @param eventName - The name of the CommBus event to unsubscribe from * @returns sendId of packet (can be used to identify packet when exception event occurs) */ - unsubscribeToCommBusEvent(eventName: string): number { + callCommBusEvent(eventName: string, broadcastTo: CommBusBroadcastTo, payload: string): number { if (this._ourProtocol < Protocol.SunRise) throw Error(SimConnectError.BadVersion); - const packet = this._beginPacket(0x61).putString(eventName, SimConnectConstants.MAX_PATH); + const packet = this._beginPacket(0x6c) + .putString256(eventName) + .putUint32(broadcastTo) + .putUint32(payload.length) + .putString(payload); return this._buildAndSend(packet); } @@ -2059,9 +2062,21 @@ class SimConnectConnection extends EventEmitter { case RecvID.ID_FLOW_EVENT: this.emit('flowEvent', new RecvFlowEvent(data)); break; + case RecvID.ID_CAMERA_DATA: + // TODO + break; + case RecvID.ID_CAMERA_STATUS: + // TODO + break; + case RecvID.ID_CAMERA_DEFINITION_LIST: + // TODO + break; case RecvID.ID_COMM_BUS: this.emit('commBusEvent', new RecvCommBus(data)); break; + case RecvID.ID_CAMERA_WORLD_LOCKER: + // TODO + break; } } diff --git a/src/SimConnectSocket.ts b/src/SimConnectSocket.ts index c8ac143..b541f3e 100644 --- a/src/SimConnectSocket.ts +++ b/src/SimConnectSocket.ts @@ -47,12 +47,11 @@ enum RecvID { ID_ENUMERATE_INPUT_EVENT_PARAMS, ID_ENUMERATE_SIMOBJECT_AND_LIVERY_LIST, ID_FLOW_EVENT, - // MSFS 2024 (SunRise): - ID_CAMERA_DATA = 40, - ID_CAMERA_DEFINITION_LIST = 41, - ID_CAMERA_STATUS = 42, - ID_CAMERA_WORLD_LOCKER = 43, - ID_COMM_BUS = 44, + ID_CAMERA_DATA, + ID_CAMERA_STATUS, + ID_CAMERA_DEFINITION_LIST, + ID_COMM_BUS, + ID_CAMERA_WORLD_LOCKER, } interface SimConnectMessage { diff --git a/src/recv/RecvCommBus.ts b/src/recv/RecvCommBus.ts index 78eceef..29e1775 100644 --- a/src/recv/RecvCommBus.ts +++ b/src/recv/RecvCommBus.ts @@ -1,15 +1,14 @@ -import { SimConnectConstants } from '../SimConnectConstants'; import { RawBuffer } from '../RawBuffer'; +import { RecvListTemplate } from './RecvListTemplate'; -export class RecvCommBus { - /** The name of the CommBus event */ - eventName: string; +export class RecvCommBus extends RecvListTemplate { + eventId: number; - /** The data payload associated with the CommBus event, decoded as a string */ data: string; constructor(data: RawBuffer) { - this.eventName = data.readString(SimConnectConstants.MAX_PATH); - this.data = data.readBytes(data.remaining()).toString('latin1'); + super(data); + this.eventId = data.readUint32(); + this.data = data.readStringV(); } } From fdb529ec9e5afdabe84b6887d08474a9a2d56b2f Mon Sep 17 00:00:00 2001 From: Even Rognlien Date: Mon, 4 May 2026 21:55:23 +0200 Subject: [PATCH 13/13] Add sample for enumerating sim objects --- samples/typescript/enumerateSimobjets.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/samples/typescript/enumerateSimobjets.ts b/samples/typescript/enumerateSimobjets.ts index e69de29..8801b7e 100644 --- a/samples/typescript/enumerateSimobjets.ts +++ b/samples/typescript/enumerateSimobjets.ts @@ -0,0 +1,23 @@ +import { open, Protocol, SimObjectType } from '../../dist'; + +open('My app', Protocol.SunRise) + .then(({ recvOpen, handle }) => { + console.log('Connected: ', recvOpen); + + handle.enumerateSimObjectsAndLiveries(123, SimObjectType.AIRCRAFT); + + handle.on('enumerateSimobjectAndLiveryList', recv => { + console.log(recv.simobjectLiveries); + }); + + handle.on('eventWeatherMode', recvWeatherMode => { + console.log('New weather mode:', recvWeatherMode.mode); + }); + + handle.on('eventFrame', recvEventFrame => { + // console.log('Framerate:', recvEventFrame.frameRate); + }); + }) + .catch(error => { + console.log('Failed to connect', error); + });