@honeybbq/teamspeak 1.0.0-beta.14 → 1.0.0-beta.15

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.
Files changed (127) hide show
  1. package/README.md +302 -5
  2. package/dist/client/commands.d.ts.map +1 -1
  3. package/dist/client/commands.js +5 -1
  4. package/dist/client/commands.js.map +1 -1
  5. package/dist/client/index.d.ts +1 -1
  6. package/dist/client/index.js +1 -1
  7. package/dist/client/transport/generation-window.d.ts +6 -0
  8. package/dist/client/transport/generation-window.d.ts.map +1 -1
  9. package/dist/client/transport/generation-window.js +11 -0
  10. package/dist/client/transport/generation-window.js.map +1 -1
  11. package/dist/client/transport/handler.d.ts.map +1 -1
  12. package/dist/client/transport/handler.js +31 -1
  13. package/dist/client/transport/handler.js.map +1 -1
  14. package/dist/core/api/channels.d.ts +10 -7
  15. package/dist/core/api/channels.d.ts.map +1 -1
  16. package/dist/core/api/channels.js +10 -7
  17. package/dist/core/api/channels.js.map +1 -1
  18. package/dist/core/api/command.d.ts +11 -7
  19. package/dist/core/api/command.d.ts.map +1 -1
  20. package/dist/core/api/command.js +11 -7
  21. package/dist/core/api/command.js.map +1 -1
  22. package/dist/core/api/credentials.d.ts +3 -2
  23. package/dist/core/api/credentials.d.ts.map +1 -1
  24. package/dist/core/api/credentials.js +3 -2
  25. package/dist/core/api/credentials.js.map +1 -1
  26. package/dist/core/api/files.d.ts +8 -1
  27. package/dist/core/api/files.d.ts.map +1 -1
  28. package/dist/core/api/files.js +8 -1
  29. package/dist/core/api/files.js.map +1 -1
  30. package/dist/core/api/groups.d.ts +21 -8
  31. package/dist/core/api/groups.d.ts.map +1 -1
  32. package/dist/core/api/groups.js +21 -8
  33. package/dist/core/api/groups.js.map +1 -1
  34. package/dist/core/api/server.d.ts +18 -6
  35. package/dist/core/api/server.d.ts.map +1 -1
  36. package/dist/core/api/server.js +18 -6
  37. package/dist/core/api/server.js.map +1 -1
  38. package/dist/core/command/responses.d.ts +8 -3
  39. package/dist/core/command/responses.d.ts.map +1 -1
  40. package/dist/core/command/responses.js +11 -3
  41. package/dist/core/command/responses.js.map +1 -1
  42. package/dist/core/command-name.d.ts +12 -0
  43. package/dist/core/command-name.d.ts.map +1 -1
  44. package/dist/core/command-name.js +12 -0
  45. package/dist/core/command-name.js.map +1 -1
  46. package/dist/core/errorcode.d.ts +8 -0
  47. package/dist/core/errorcode.d.ts.map +1 -1
  48. package/dist/core/errorcode.js +8 -0
  49. package/dist/core/errorcode.js.map +1 -1
  50. package/dist/core/errors.d.ts +7 -6
  51. package/dist/core/errors.d.ts.map +1 -1
  52. package/dist/core/errors.js +7 -6
  53. package/dist/core/errors.js.map +1 -1
  54. package/dist/core/notification-name.d.ts +16 -0
  55. package/dist/core/notification-name.d.ts.map +1 -1
  56. package/dist/core/notification-name.js +16 -0
  57. package/dist/core/notification-name.js.map +1 -1
  58. package/dist/query/client.d.ts.map +1 -1
  59. package/dist/query/client.js +9 -6
  60. package/dist/query/client.js.map +1 -1
  61. package/dist/query/late-rows.d.ts +6 -4
  62. package/dist/query/late-rows.d.ts.map +1 -1
  63. package/dist/query/late-rows.js +6 -4
  64. package/dist/query/late-rows.js.map +1 -1
  65. package/dist/stream/emitter.d.ts +18 -0
  66. package/dist/stream/emitter.d.ts.map +1 -0
  67. package/dist/stream/emitter.js +66 -0
  68. package/dist/stream/emitter.js.map +1 -0
  69. package/dist/stream/errors.d.ts +73 -0
  70. package/dist/stream/errors.d.ts.map +1 -0
  71. package/dist/stream/errors.js +128 -0
  72. package/dist/stream/errors.js.map +1 -0
  73. package/dist/stream/index.d.ts +27 -0
  74. package/dist/stream/index.d.ts.map +1 -0
  75. package/dist/stream/index.js +26 -0
  76. package/dist/stream/index.js.map +1 -0
  77. package/dist/stream/peer.d.ts +43 -0
  78. package/dist/stream/peer.d.ts.map +1 -0
  79. package/dist/stream/peer.js +100 -0
  80. package/dist/stream/peer.js.map +1 -0
  81. package/dist/stream/published.d.ts +54 -0
  82. package/dist/stream/published.d.ts.map +1 -0
  83. package/dist/stream/published.js +205 -0
  84. package/dist/stream/published.js.map +1 -0
  85. package/dist/stream/streams.d.ts +92 -0
  86. package/dist/stream/streams.d.ts.map +1 -0
  87. package/dist/stream/streams.js +671 -0
  88. package/dist/stream/streams.js.map +1 -0
  89. package/dist/stream/types.d.ts +428 -0
  90. package/dist/stream/types.d.ts.map +1 -0
  91. package/dist/stream/types.js +2 -0
  92. package/dist/stream/types.js.map +1 -0
  93. package/dist/stream/wait.d.ts +14 -0
  94. package/dist/stream/wait.d.ts.map +1 -0
  95. package/dist/stream/wait.js +46 -0
  96. package/dist/stream/wait.js.map +1 -0
  97. package/dist/stream/wire.d.ts +91 -0
  98. package/dist/stream/wire.d.ts.map +1 -0
  99. package/dist/stream/wire.js +248 -0
  100. package/dist/stream/wire.js.map +1 -0
  101. package/package.json +13 -6
  102. package/src/client/commands.ts +4 -1
  103. package/src/client/index.ts +1 -1
  104. package/src/client/transport/generation-window.ts +11 -0
  105. package/src/client/transport/handler.ts +28 -2
  106. package/src/core/api/channels.ts +10 -7
  107. package/src/core/api/command.ts +11 -7
  108. package/src/core/api/credentials.ts +3 -2
  109. package/src/core/api/files.ts +8 -1
  110. package/src/core/api/groups.ts +21 -8
  111. package/src/core/api/server.ts +18 -6
  112. package/src/core/command/responses.ts +14 -3
  113. package/src/core/command-name.ts +12 -0
  114. package/src/core/errorcode.ts +8 -0
  115. package/src/core/errors.ts +7 -6
  116. package/src/core/notification-name.ts +16 -0
  117. package/src/query/client.ts +9 -6
  118. package/src/query/late-rows.ts +6 -4
  119. package/src/stream/emitter.ts +73 -0
  120. package/src/stream/errors.ts +146 -0
  121. package/src/stream/index.ts +58 -0
  122. package/src/stream/peer.ts +141 -0
  123. package/src/stream/published.ts +254 -0
  124. package/src/stream/streams.ts +765 -0
  125. package/src/stream/types.ts +475 -0
  126. package/src/stream/wait.ts +62 -0
  127. package/src/stream/wire.ts +319 -0
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # @honeybbq/teamspeak
4
4
 
5
- **A clean-room TeamSpeak client protocol library written in pure TypeScript.**
5
+ **An independent TeamSpeak client protocol library written in pure TypeScript.**
6
6
 
7
7
  Works with TeamSpeak 3 and TeamSpeak 6 servers. No proprietary SDK, no copied code.
8
8
 
@@ -32,6 +32,7 @@ Upgrading from 0.x? The package was renamed; see the [migration guide](https://g
32
32
  - **Identity management** — Generate, import/export, and upgrade security level of identities
33
33
  - **Full command set** — 111 typed functions covering every command the voice protocol answers
34
34
  - **Reconnection** — Opt-in retry with backoff, tuned for TeamSpeak's antiflood ban
35
+ - **Streams** — TeamSpeak 6 screen share signalling: find, publish and watch streams, with SDP and ICE as strings for any WebRTC stack (experimental)
35
36
  - **Server capabilities** — Reports the server version and the uid hash it uses (SHA-1 on TS3, SHA-256 on TS6), and the server's name, icon, banner and welcome message
36
37
  - **Custom transports** — Supply your own socket, for proxies, tunnels or tests
37
38
  - **ESM with types** — Ships ES modules with full TypeScript declarations
@@ -548,6 +549,297 @@ The uid hash matters because TS3 hashes a client's public key with SHA-1 and TS6
548
549
  with SHA-256 — a client that assumes either one computes an identifier the other
549
550
  server never sends.
550
551
 
552
+ ### Streams (experimental)
553
+
554
+ TeamSpeak 6 clients can share a screen, a window or a camera. `@honeybbq/teamspeak/stream`
555
+ does the signalling of that: it finds the streams of the clients in view, publishes one,
556
+ and watches one. The video itself travels between the publisher and each viewer over
557
+ WebRTC, which is yours: `Streams` hands you the SDP and the ICE candidates as strings and
558
+ takes them back, so any WebRTC stack plugs in, in a browser, in Electron or in Node. The
559
+ package has no WebRTC dependency.
560
+
561
+ TeamSpeak does not document this protocol. It was measured on TeamSpeak 6 6.0.0-beta13.1,
562
+ and has not been tried against the native TeamSpeak 6 client yet, so every export is
563
+ `@beta` and may change in a minor release. A TeamSpeak 3 server does not answer the stream
564
+ commands at all, so `publish` and `watch` throw a `StreamsUnsupportedError` before sending
565
+ anything.
566
+
567
+ Watching: find a stream in `streams.list()` or wait for `started`, ask to watch it, and
568
+ give the publisher's offer to a peer connection. The viewer's connection is receive-only.
569
+
570
+ ```ts
571
+ import { Streams } from "@honeybbq/teamspeak/stream";
572
+
573
+ const streams = new Streams(client);
574
+ streams.on("started", (info) => console.log(`${info.name} is streaming`));
575
+
576
+ const view = await streams.watch(info, { message: "may I watch?" });
577
+
578
+ const pc = new RTCPeerConnection({ iceServers: [{ urls: "stun:stun.l.google.com:19302" }] });
579
+ pc.ontrack = (event) => (video.srcObject = event.streams[0]);
580
+ pc.onicecandidate = (event) => {
581
+ if (event.candidate) void view.sendCandidate(event.candidate.toJSON());
582
+ };
583
+ // Candidates that arrived with the offer are delivered to the first listener.
584
+ view.on("candidate", (candidate) => void pc.addIceCandidate(candidate));
585
+ view.on("closed", () => pc.close());
586
+
587
+ await pc.setRemoteDescription({ type: "offer", sdp: view.offer });
588
+ const answer = await pc.createAnswer();
589
+ await pc.setLocalDescription(answer);
590
+ await view.answer(answer.sdp!);
591
+ ```
592
+
593
+ Publishing: the publisher makes the offer, one peer connection for each viewer. The server
594
+ relays every request to watch, from any channel, and enforces neither `viewerLimit` nor
595
+ `accessibility`: deciding who is let in is the publisher's job, and `request` is emitted
596
+ for each one, with nothing accepted for you.
597
+
598
+ ```ts
599
+ const stream = await streams.publish({ name: "My screen", source: "screen" });
600
+
601
+ stream.on("request", async (request) => {
602
+ if (!allowed(request.viewer)) return request.deny("Not today");
603
+
604
+ const pc = new RTCPeerConnection({ iceServers });
605
+ for (const track of capture.getTracks()) pc.addTrack(track, capture);
606
+ await pc.setLocalDescription(await pc.createOffer());
607
+
608
+ const viewer = await request.accept(pc.localDescription!.sdp);
609
+ pc.onicecandidate = (event) => {
610
+ if (event.candidate) void viewer.sendCandidate(event.candidate.toJSON());
611
+ };
612
+ viewer.on("answer", (sdp) => pc.setRemoteDescription({ type: "answer", sdp }));
613
+ viewer.on("candidate", (candidate) => void pc.addIceCandidate(candidate));
614
+ viewer.on("closed", () => pc.close());
615
+ });
616
+
617
+ await stream.stop();
618
+ ```
619
+
620
+ Things to know:
621
+
622
+ - A client may run several streams, so every stream is known by its id. `list()` is what
623
+ this client has heard of. A stream that ends while its publisher is out of the channel is
624
+ reported to nobody there, and stays listed until the connection ends.
625
+ - A stream survives its publisher changing channel, with no event. It ends when the
626
+ publisher stops it or disconnects.
627
+ - The server drops a command line longer than about 9.2 KB without an answer. Streams
628
+ commands are checked first and throw a `StreamPayloadTooLargeError`; an SDP offer or an
629
+ ICE candidate is a few kilobytes at most.
630
+ - The server lets anyone stop any stream. `Streams` sends `stopstream` only for streams it
631
+ published, and treats a `notifystreamstopped` for any stream as final.
632
+ - The server remembers no decision, so a client can ask again, and every ask is relayed.
633
+ To refuse for good, pass `rememberDenied: true` (the 1024 most recent uids, in memory
634
+ only; a requester out of view is looked up with `clientgetuidfromclid`), or leave the
635
+ request unanswered. The server has no way to remove a viewer: closing a viewer's peer
636
+ ends your side of it, and stopping the stream ends it for everyone.
637
+ - `dispose()` stops the streams published through that `Streams` and leaves the ones it
638
+ watched, while the client is connected. A stream whose `publish` gave up after the command
639
+ went out is stopped when its announcement arrives, so none is left running unseen.
640
+ - A client whose `client_is_streaming` clears, or that comes into view not streaming, has
641
+ its streams dropped, after a grace of two seconds: `stopped` with the cause `"gone"`. The
642
+ server clears the flag right before it reports the stop, so a stop that arrives in that time
643
+ ends the stream with its own cause and reason instead, and a flag set again cancels the drop.
644
+ - The bitrate the server reports for a stream is not the one requested.
645
+ - A client announces that it streams only with `client_is_streaming`, and a stream that was
646
+ running before you came is not announced at all. `Streams` asks for the streams of each
647
+ client that sets the flag, once, on a TeamSpeak 6 server.
648
+
649
+ ## Design
650
+
651
+ The conventions below hold across the whole package. Each is stated once here.
652
+
653
+ ### Errors
654
+
655
+ An error is one of two kinds:
656
+
657
+ - **A refusal the server reports.** It is a `ServerError` carrying the server's
658
+ numeric `code`, its text as `serverMessage`, and `extraMessage` and
659
+ `failedPermissionID` when the server names them.
660
+ - **A condition the SDK detects itself**: not connected, no data, a timeout, a
661
+ failed handshake, a failed file transfer. Each has its own class.
662
+
663
+ Every error the SDK throws on purpose extends `TeamspeakError`, so
664
+ `instanceof TeamspeakError` tells its failures from anything else. A failure
665
+ that wraps another keeps it in `cause`. Match a refusal by `error.code` against
666
+ `ErrorCode`, and everything else by class:
667
+
668
+ ```typescript
669
+ import {
670
+ ErrorCode,
671
+ NotConnectedError,
672
+ ServerError,
673
+ TeamspeakError,
674
+ asChannelID,
675
+ moveClient,
676
+ } from "@honeybbq/teamspeak";
677
+
678
+ try {
679
+ await moveClient(client, client.clientID, asChannelID(5), { password: "wrong" });
680
+ } catch (err) {
681
+ if (err instanceof ServerError && err.code === ErrorCode.ChannelInvalidPassword) {
682
+ // ask for the password again
683
+ } else if (err instanceof NotConnectedError) {
684
+ // the connection is gone
685
+ } else if (err instanceof ServerError) {
686
+ console.log(err.code, err.serverMessage, err.extraMessage, err.failedPermissionID);
687
+ } else if (err instanceof TeamspeakError) {
688
+ console.log(err.name, err.message, err.cause);
689
+ } else {
690
+ throw err;
691
+ }
692
+ }
693
+ ```
694
+
695
+ `ErrorCode` names the server's codes as constants. It is the table `errcode`
696
+ is in the Go SDK, with the same numbers. A server may send a code it does not
697
+ name, so `code` is a plain `number`. `instanceof` also works between two
698
+ installed copies of the package.
699
+
700
+ Errors live with the code that produces them, so each entry point exports only
701
+ the errors its own code throws:
702
+
703
+ | Entry point | Errors |
704
+ | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
705
+ | `@honeybbq/teamspeak` (shared by every connection) | `TeamspeakError`, `ServerError`, `CommandTimeoutError`, `NotConnectedError`, `AlreadyConnectedError`, `NoDataError` |
706
+ | `@honeybbq/teamspeak/client` (voice only) | `ConnectionTimeoutError`, `HandshakeError` and its `PuzzleTooHardError`, `InvalidAddressError`, `InvalidIdentityError`, `FileTransferError`, `RemovedError` (kick, ban or server stop) |
707
+ | `@honeybbq/teamspeak/stream` (streams only) | `StreamsUnsupportedError`, `StreamJoinDeniedError`, `StreamEndedError`, `StreamPayloadTooLargeError` |
708
+ | `@honeybbq/teamspeak/query` (query only) | `QueryAuthError`, `GuestAccessError`, `SshHostKeyError`, `MissingDependencyError`, `WebQueryHttpError` |
709
+
710
+ A call that is given a value it cannot use, such as an out-of-range id or an
711
+ option that contradicts another, throws the language's own `TypeError` or
712
+ `RangeError` before anything is sent. Those are not `TeamspeakError`s: they are
713
+ a bug in the caller, not a condition to handle. `WebQueryHttpError` carries the
714
+ HTTP `status`; a refusal the server itself reports is a `ServerError` over every
715
+ transport.
716
+
717
+ **Cancellation.** An aborted `signal` is the caller's own cancellation, not an
718
+ SDK error. `connect()`, the typed commands, `exec`, file transfers and `events()`
719
+ reject with the signal's `reason` itself, unwrapped: a `DOMException` named
720
+ `AbortError` after a bare `abort()`, a `TimeoutError` after
721
+ `AbortSignal.timeout()`, or whatever value was passed to `abort(reason)`. A
722
+ signal that is already aborted rejects before anything is sent. Aborting a
723
+ `connect()` abandons the attempt and leaves the client disconnected; a command
724
+ that was given up on may still have taken effect on the server. The signal and
725
+ the command's own `timeoutMs` are separate: when `timeoutMs` (ten seconds by
726
+ default) passes first, the command rejects with a `CommandTimeoutError`.
727
+
728
+ **No data.** `NoDataError` means the command succeeded but its answer lacked the
729
+ data it exists to return, such as the id of a channel just created. Treat the
730
+ command as having taken effect and do not retry it blindly, or it creates a
731
+ second object or is refused as a duplicate. `createChannel` and the group
732
+ commands look the new object up themselves and throw it only when nothing
733
+ matches. A list that comes back empty is `[]`, not this error.
734
+
735
+ ### Ids
736
+
737
+ Every kind of id is a branded type: `ChannelID`, `ClientID`, `ClientDBID`,
738
+ `ServerGroupID`, `ChannelGroupID`, `PermissionID`, `IconID`, `ServerID`,
739
+ `BanID` and the rest, with `UID` and `ChannelUID` as branded strings. The
740
+ brand costs nothing at runtime, and passing the wrong kind is a compile error.
741
+ Make one with its constructor, which throws a `RangeError` for a value outside
742
+ the id's wire range:
743
+
744
+ ```typescript
745
+ import { asChannelID, asClientID, moveClient } from "@honeybbq/teamspeak";
746
+
747
+ const cid = asChannelID(5); // or asChannelID(5n)
748
+ await moveClient(client, client.clientID, cid);
749
+ // moveClient(client, cid, client.clientID) does not compile.
750
+ const clid = asClientID(3);
751
+ ```
752
+
753
+ A `ClientID`, `PermissionID`, `IconID` and the file transfer ids are `number`s.
754
+ The ones the protocol makes 64 bits wide are `bigint`s: `ChannelID`,
755
+ `ClientDBID`, `ServerGroupID`, `ChannelGroupID`, `ServerID`, `BanID`,
756
+ `MessageID` and `APIKeyID`. Their constructors take a `number` or a `bigint`.
757
+
758
+ ### Options and absent values
759
+
760
+ **Options.** Required arguments are positional. Everything optional is one
761
+ options object, the last argument. A field that is not set is not sent:
762
+ `undefined` leaves the setting alone and `""` sends it empty.
763
+
764
+ ```typescript
765
+ import { editChannel } from "@honeybbq/teamspeak";
766
+
767
+ await editChannel(client, cid, {
768
+ name: "Lounge",
769
+ topic: "", // sent empty: clears the topic
770
+ // password is not set: not sent, so the password stays as it is.
771
+ });
772
+ ```
773
+
774
+ **Records.** In a record the server's answer fills in, an optional field is
775
+ `undefined` when the server did not send it. That is not the same as `0` or
776
+ `false`: `totalClients` is `undefined` in a `getChannelInfo` result, which does
777
+ not carry it, and `0` in a `listChannels` result of an empty channel.
778
+
779
+ ```typescript
780
+ import { getChannelInfo } from "@honeybbq/teamspeak";
781
+
782
+ const info = await getChannelInfo(client, cid);
783
+ if (info.totalClients !== undefined) console.log(info.totalClients, "clients");
784
+ ```
785
+
786
+ **Durations and times** are numbers of milliseconds with an `Ms` suffix
787
+ (`idleTimeMs`, `timeoutMs`) and `Date`. A `Date` field is `undefined` where the
788
+ server sends 0 for "never". The Go SDK uses `time.Duration` and `time.Time`.
789
+
790
+ ### Events
791
+
792
+ Each server event has a typed name (`"textMessage"`, `"clientEnter"`, ...;
793
+ `EventName` has a constant for each), and `"notification"` delivers every
794
+ notification as the server sent it, including those no typed event models. The
795
+ notification arrives after the typed event it produced. `on()` returns a
796
+ function that removes the listener; `events(name, { signal })` is the same as an
797
+ async iterator.
798
+
799
+ ```typescript
800
+ const off = client.on("textMessage", (message) => {
801
+ console.log(message.invokerName, message.message);
802
+ });
803
+ off();
804
+
805
+ client.on("notification", (n) => console.log(n.name, n.params));
806
+ ```
807
+
808
+ - **Order.** Listeners run one at a time, in the order the events arrived, so a
809
+ listener may call the client and await an answer.
810
+ - **State.** The client updates its state, such as `client.view`, before it
811
+ queues the event that reports the change, so a listener sees state at least as
812
+ new as its event.
813
+ - **A throw** in a listener does not stop the others. The exception is rethrown
814
+ from a microtask, as an uncaught exception, as an EventEmitter listener's
815
+ would be. The Go SDK recovers a panicking handler and logs it instead.
816
+ - **Middleware.** `eventMiddleware` sees each server event before the listeners
817
+ and may pass it on, replace it or drop it; `connected`, `disconnected`,
818
+ `kicked` and the view events bypass it. `commandMiddleware` wraps the typed commands. Both are
819
+ described above.
820
+
821
+ ### Stability
822
+
823
+ From 1.0 the package follows semver, as described under
824
+ [Compatibility](#compatibility): a breaking change to `@honeybbq/teamspeak`,
825
+ `/client` and `/versions` waits for 2.0. The enumerations, `ErrorCode` and the
826
+ event map may gain members in a minor release; give a `switch` over them a
827
+ default case.
828
+
829
+ An API that may still change in a minor release is marked `@experimental` (or, for
830
+ `@honeybbq/teamspeak/stream`, `@beta`) in its TSDoc, which editors and the API reference
831
+ show. Today that is the whole `@honeybbq/teamspeak/stream` entry point, and the whole
832
+ `@honeybbq/teamspeak/query` entry point, the server view (`client.view` and the
833
+ view events), the `previous` field of the client-left and channel-deleted
834
+ events, and `getServerVariables`.
835
+
836
+ ### Server behaviour the SDK works around
837
+
838
+ The servers answer some commands in ways that differ between TeamSpeak 3 and 6
839
+ and between transports, and the SDK evens those out. The measured tables are in
840
+ [voice-protocol-responses.md](https://github.com/HoneyBBQ/teamspeak-js/blob/main/docs/voice-protocol-responses.md)
841
+ and [serverquery-responses.md](https://github.com/HoneyBBQ/teamspeak-js/blob/main/docs/serverquery-responses.md).
842
+
551
843
  ## ServerQuery
552
844
 
553
845
  The `@experimental` subpath `@honeybbq/teamspeak/query` administers a server
@@ -751,6 +1043,7 @@ with the same semantics, apart from what the languages make differ:
751
1043
  | `@honeybbq/teamspeak` | What every TeamSpeak connection shares: the command language, ids, errors, the typed commands, the event types and the status and reconnection types |
752
1044
  | `@honeybbq/teamspeak/client` | The voice client, identities, file transfers, diagnostics and its retry policy |
753
1045
  | `@honeybbq/teamspeak/versions` | Client versions a server accepts, for the client's `version` option; data only, one export each |
1046
+ | `@honeybbq/teamspeak/stream` | TeamSpeak 6 streams, the screen share: finding, publishing and watching them; signalling only, no WebRTC code; `@beta` |
754
1047
  | `@honeybbq/teamspeak/query` | The ServerQuery client, running the same typed commands over raw TCP, SSH or WebQuery; `@experimental` |
755
1048
 
756
1049
  Each entry exports only its own symbols. The client entry does not re-export
@@ -769,7 +1062,8 @@ get no further releases. Coming from 0.x? The
769
1062
 
770
1063
  ```
771
1064
  src/
772
- ├── core/ # Shared: command language, ids, errors, typed commands
1065
+ ├── core/ # Shared (@honeybbq/teamspeak): command language, ids, errors, typed commands
1066
+ ├── versions/ # Client versions (@honeybbq/teamspeak/versions)
773
1067
  ├── client/ # The voice client (@honeybbq/teamspeak/client)
774
1068
  │ ├── client.ts # Client lifecycle, connection management
775
1069
  │ ├── commands.ts # Command sending and response tracking
@@ -785,7 +1079,8 @@ src/
785
1079
  │ ├── handshake/ # Crypto handshake and license verification
786
1080
  │ ├── transport/ # UDP packet framing, ACK, compression
787
1081
  │ └── discovery/ # SRV / TSDNS / direct address resolution
788
- └── query/ # ServerQuery client (not exported yet)
1082
+ ├── stream/ # TeamSpeak 6 streams (@honeybbq/teamspeak/stream)
1083
+ └── query/ # The ServerQuery client (@honeybbq/teamspeak/query)
789
1084
  examples/
790
1085
  └── connect.ts # Minimal connection example
791
1086
  ```
@@ -800,9 +1095,11 @@ Protocol knowledge was primarily informed by the [TSLib](https://github.com/Spla
800
1095
 
801
1096
  ## Disclaimer
802
1097
 
803
- TeamSpeak is a registered trademark of [TeamSpeak Systems GmbH](https://teamspeak.com/). This project is not affiliated with, endorsed by, or associated with TeamSpeak Systems GmbH in any way.
1098
+ TeamSpeak is a registered trademark of [TeamSpeak Systems, Inc.](https://teamspeak.com/). This project is not affiliated with, endorsed by, or associated with TeamSpeak Systems, Inc. in any way.
1099
+
1100
+ This library is an **independent implementation** built from public documentation, observation of network traffic, and independent research. It contains no TeamSpeak proprietary code, headers, libraries, or binaries.
804
1101
 
805
- This library is a **clean-room implementation** developed from publicly available documentation, protocol analysis of network traffic, and independent research. No proprietary TeamSpeak SDK code, headers, or libraries were used in its creation.
1102
+ The project implements the client side only and does not implement a TeamSpeak server.
806
1103
 
807
1104
  ## Contributing
808
1105
 
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/client/commands.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACrC;AAsBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,cAAc;;IAOzB,6EAA6E;IAC7E,YAAY,WAAW,GAAE,MAAM,MAAgB,EAE9C;IAED,QAAQ,CACN,IAAI,CAAC,EAAE,YAAY,EACnB,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,GACZ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAO/C;IAED,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE3B;IAED,2EAA2E;IAC3E,qBAAqB,IAAI,IAAI,CAG5B;IAED,oEAAoE;IACpE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAM7B;IAED,wEAAwE;IACxE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,SAAK,GAAG,IAAI,CAIxD;IAED;;;;;OAKG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAiBlE;IAED,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAkB3C;IAED,aAAa,IAAI,IAAI,CAEpB;IAED;;;;;OAKG;IACH,KAAK,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,CAOtB;CAkBF;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAMhE"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/client/commands.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACrC;AAsBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,cAAc;;IAOzB,6EAA6E;IAC7E,YAAY,WAAW,GAAE,MAAM,MAAgB,EAE9C;IAED,QAAQ,CACN,IAAI,CAAC,EAAE,YAAY,EACnB,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,GACZ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAS/C;IAED,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE3B;IAED,2EAA2E;IAC3E,qBAAqB,IAAI,IAAI,CAG5B;IAED,oEAAoE;IACpE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAM7B;IAED,wEAAwE;IACxE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,SAAK,GAAG,IAAI,CAIxD;IAED;;;;;OAKG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAiBlE;IAED,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAkB3C;IAED,aAAa,IAAI,IAAI,CAEpB;IAED;;;;;OAKG;IACH,KAAK,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,CAQtB;CAkBF;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAMhE"}
@@ -35,6 +35,9 @@ export class CommandTracker {
35
35
  register(spec, correlation, echo) {
36
36
  this.#nextRC++;
37
37
  const rc = this.#nextRC;
38
+ // The tracker's own number is the one request field no caller can supply.
39
+ if (spec?.correlated?.key === "return_code")
40
+ correlation = String(rc);
38
41
  const promise = new Promise((resolve) => {
39
42
  this.#pending.set(rc, { resolve, spec, correlation, echo, correlated: [] });
40
43
  });
@@ -127,7 +130,8 @@ export class CommandTracker {
127
130
  p.resolve({ err, data: [] });
128
131
  this.#buffer = [];
129
132
  this.#welcomeComplete = false;
130
- this.#nextRC = 0;
133
+ // Not restarted: a row still in flight from before the reset carries an old
134
+ // number, which a restarted counter would hand to an unrelated command.
131
135
  }
132
136
  #answers(p, row) {
133
137
  if (row.source === "")
@@ -1 +1 @@
1
- {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/client/commands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/client/commands.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * A clean-room TeamSpeak client: it connects over the voice protocol, the way
2
+ * An independent TeamSpeak client: it connects over the voice protocol, the way
3
3
  * the TeamSpeak application does.
4
4
  *
5
5
  * Only what belongs to the voice protocol is exported here. The typed commands,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * A clean-room TeamSpeak client: it connects over the voice protocol, the way
2
+ * An independent TeamSpeak client: it connects over the voice protocol, the way
3
3
  * the TeamSpeak application does.
4
4
  *
5
5
  * Only what belongs to the voice protocol is exported here. The typed commands,
@@ -4,6 +4,12 @@ export declare class GenerationWindow {
4
4
  get generation(): number;
5
5
  advance(amount: number): void;
6
6
  advanceToExcluded(mappedValue: number): void;
7
+ /**
8
+ * Move the base to just past a packet that was just received. A stale packet,
9
+ * including one from the generation before a wrap, does not move the base
10
+ * backwards or treat the wait for the next generation as a full cycle.
11
+ */
12
+ advanceAfter(mappedValue: number): void;
7
13
  syncTo(mappedValue: number): void;
8
14
  isInWindow(mappedValue: number): boolean;
9
15
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"generation-window.d.ts","sourceRoot":"","sources":["../../../src/client/transport/generation-window.ts"],"names":[],"mappings":"AAAA,qBAAa,gBAAgB;;IAM3B,YAAY,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAG1C;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAQ5B;IAED,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAI3C;IAED,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAIhC;IAED,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAMvC;IAED;;;;OAIG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAMzC;IAgBD;;;;OAIG;IACH,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAExC;IAED,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE3C;IASD;;;;OAIG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAIzC;IAED,KAAK,IAAI,IAAI,CAGZ;CACF"}
1
+ {"version":3,"file":"generation-window.d.ts","sourceRoot":"","sources":["../../../src/client/transport/generation-window.ts"],"names":[],"mappings":"AAAA,qBAAa,gBAAgB;;IAM3B,YAAY,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAG1C;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAQ5B;IAED,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAI3C;IAED;;;;OAIG;IACH,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAItC;IAED,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAIhC;IAED,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAMvC;IAED;;;;OAIG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAMzC;IAgBD;;;;OAIG;IACH,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAExC;IAED,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE3C;IASD;;;;OAIG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAIzC;IAED,KAAK,IAAI,IAAI,CAGZ;CACF"}
@@ -26,6 +26,17 @@ export class GenerationWindow {
26
26
  moveDist += this.#mod;
27
27
  this.advance(moveDist + 1);
28
28
  }
29
+ /**
30
+ * Move the base to just past a packet that was just received. A stale packet,
31
+ * including one from the generation before a wrap, does not move the base
32
+ * backwards or treat the wait for the next generation as a full cycle.
33
+ */
34
+ advanceAfter(mappedValue) {
35
+ const index = this.mappedToIndex(mappedValue);
36
+ if (index < 0)
37
+ return;
38
+ this.advance(index + 1);
39
+ }
29
40
  syncTo(mappedValue) {
30
41
  let moveDist = mappedValue - this.#mappedBaseOffset;
31
42
  if (moveDist < 0)
@@ -1 +1 @@
1
- {"version":3,"file":"generation-window.js","sourceRoot":"","sources":["../../../src/client/transport/generation-window.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generation-window.js","sourceRoot":"","sources":["../../../src/client/transport/generation-window.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/client/transport/handler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EACL,KAAK,MAAM,EACX,UAAU,EAMX,MAAM,aAAa,CAAC;AAMrB,OAAO,EAAoC,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAwBtF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAa,CAAC;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAgB,CAAC;AA4BhD,qBAAa,aAAa;;IACxB,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAC9C,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IACtD;;;;OAIG;IACH,cAAc,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IACzD,2FAA2F;IAC3F,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IA8CzD,YAAY,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,MAAmB,EAAE,aAAa,CAAC,EAAE,aAAa,EAInF;IAED,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE5B;IAED;;;;;;;OAOG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAyD/D;IAOD,sGAAsG;IACtG,KAAK,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CA+ChC;IAED;;;;;;OAMG;IACH,oBAAoB,IAAI,IAAI,CAE3B;IAED,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAOnE;IAED,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CA6BrD;IAED;;;;OAIG;IACH,iBAAiB,CACf,IAAI,EAAE,UAAU,EAChB,eAAe,EAAE,SAAS,MAAM,EAAE,EAClC,KAAK,EAAE,MAAM,EACb,gBAAgB,GAAE,SAAS,MAAM,EAAO,GACvC,IAAI,CAiCN;IAED;;;;OAIG;IACH,sBAAsB,CACpB,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,GACZ,IAAI,CAiBN;IA4BD,wEAAwE;IACxE,eAAe,IAAI,eAAe,CAEjC;IAED,oEAAoE;IACpE,IAAI,SAAS,IAAI;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAEhF;IAED,4EAA4E;IAC5E,iFAAiF;IACjF,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,iDAAiD;IACjD,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,KAAK,IAAI,IAAI,CAmBZ;CA8gBF"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/client/transport/handler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EACL,KAAK,MAAM,EACX,UAAU,EAMX,MAAM,aAAa,CAAC;AAMrB,OAAO,EAAoC,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AA8BtF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAa,CAAC;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAgB,CAAC;AA4BhD,qBAAa,aAAa;;IACxB,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAC9C,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IACtD;;;;OAIG;IACH,cAAc,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IACzD,2FAA2F;IAC3F,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAkDzD,YAAY,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,MAAmB,EAAE,aAAa,CAAC,EAAE,aAAa,EAInF;IAED,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE5B;IAED;;;;;;;OAOG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAyD/D;IAOD,sGAAsG;IACtG,KAAK,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CA+ChC;IAED;;;;;;OAMG;IACH,oBAAoB,IAAI,IAAI,CAE3B;IAED,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAOnE;IAED,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CA6BrD;IAED;;;;OAIG;IACH,iBAAiB,CACf,IAAI,EAAE,UAAU,EAChB,eAAe,EAAE,SAAS,MAAM,EAAE,EAClC,KAAK,EAAE,MAAM,EACb,gBAAgB,GAAE,SAAS,MAAM,EAAO,GACvC,IAAI,CAiCN;IAED;;;;OAIG;IACH,sBAAsB,CACpB,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,GACZ,IAAI,CAiBN;IA4BD,wEAAwE;IACxE,eAAe,IAAI,eAAe,CAEjC;IAED,oEAAoE;IACpE,IAAI,SAAS,IAAI;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAEhF;IAED,4EAA4E;IAC5E,iFAAiF;IACjF,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,iDAAiD;IACjD,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,KAAK,IAAI,IAAI,CAmBZ;CA8hBF"}
@@ -12,6 +12,12 @@ import { RoundTripEstimator, TrafficMeter } from "./traffic.js";
12
12
  import { processInit1 } from "../handshake/crypt-handshake.js";
13
13
  const MAX_OUT_PACKET_SIZE = 500;
14
14
  const RECEIVE_PACKET_WINDOW_SIZE = 1024;
15
+ // Voice, whisper, ping and pong are never sent again, so any number of them can
16
+ // be lost in a row. Their window spans half the id space: a packet's generation
17
+ // is the one that puts it nearest the last packet received, as long as fewer
18
+ // than 32768 were lost, where a narrower window would place the first packet
19
+ // after a long loss across the wrap in the old generation and never recover.
20
+ const UNACKED_RECEIVE_WINDOW_SIZE = 1 << 15;
15
21
  const PING_INTERVAL_MS = 5_000;
16
22
  const PACKET_TIMEOUT_MS = 60_000;
17
23
  const MAX_RETRY_INTERVAL_MS = 1_000;
@@ -87,6 +93,10 @@ export class PacketHandler {
87
93
  #generationCounter = new Uint32Array(9);
88
94
  #recvWindowCommand = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);
89
95
  #recvWindowCommandLow = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);
96
+ #recvWindowVoice = new GenerationWindow(1 << 16, UNACKED_RECEIVE_WINDOW_SIZE);
97
+ #recvWindowVoiceWhisper = new GenerationWindow(1 << 16, UNACKED_RECEIVE_WINDOW_SIZE);
98
+ #recvWindowPing = new GenerationWindow(1 << 16, UNACKED_RECEIVE_WINDOW_SIZE);
99
+ #recvWindowPong = new GenerationWindow(1 << 16, UNACKED_RECEIVE_WINDOW_SIZE);
90
100
  #sendWindowCommand = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);
91
101
  #sendWindowCommandLow = new GenerationWindow(1 << 16, RECEIVE_PACKET_WINDOW_SIZE);
92
102
  #commandQueue = new Map();
@@ -505,11 +515,12 @@ export class PacketHandler {
505
515
  const decrypted = this.#decryptPacketData(p, header, ciphertext, tag);
506
516
  if (decrypted === null)
507
517
  return;
518
+ const pType = packetType(p);
519
+ this.#advanceReceiveGeneration(pType, p.id);
508
520
  p.data = decrypted.plaintext;
509
521
  // Only a packet that decrypted shows the server is there: anyone can send
510
522
  // datagrams that do not, and they must not keep a dead session alive.
511
523
  this.#lastMessageReceived = performance.now();
512
- const pType = packetType(p);
513
524
  if (pType === PacketType.Pong && p.data.length >= 2) {
514
525
  const acked = new DataView(p.data.buffer, p.data.byteOffset).getUint16(0, false);
515
526
  const sentAt = this.#pingsSent.get(acked);
@@ -538,14 +549,33 @@ export class PacketHandler {
538
549
  case PacketType.AckLow:
539
550
  return this.#sendWindowCommandLow.getGeneration(id);
540
551
  case PacketType.Voice:
552
+ return this.#recvWindowVoice.getGeneration(id);
541
553
  case PacketType.VoiceWhisper:
554
+ return this.#recvWindowVoiceWhisper.getGeneration(id);
542
555
  case PacketType.Ping:
556
+ return this.#recvWindowPing.getGeneration(id);
543
557
  case PacketType.Pong:
558
+ return this.#recvWindowPong.getGeneration(id);
544
559
  case PacketType.Init1:
545
560
  default:
546
561
  return 0;
547
562
  }
548
563
  }
564
+ /**
565
+ * Follow the packet id of an authenticated non-command packet. Voice has no
566
+ * retransmission or acknowledgement window, but its 16-bit id still wraps
567
+ * and changes the generation used by the encryption key.
568
+ */
569
+ #advanceReceiveGeneration(pType, id) {
570
+ if (pType === PacketType.Voice)
571
+ this.#recvWindowVoice.advanceAfter(id);
572
+ if (pType === PacketType.VoiceWhisper)
573
+ this.#recvWindowVoiceWhisper.advanceAfter(id);
574
+ if (pType === PacketType.Ping)
575
+ this.#recvWindowPing.advanceAfter(id);
576
+ if (pType === PacketType.Pong)
577
+ this.#recvWindowPong.advanceAfter(id);
578
+ }
549
579
  #decryptPacketData(p, header, ciphertext, tag) {
550
580
  const unencrypted = (packetFlags(p) & PacketFlags.Unencrypted) !== 0;
551
581
  const dummy = !this.#crypt.cryptoInitComplete;