@jsprismarine/protocol 0.14.2 → 0.15.0-unstable-20261024032303

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 (187) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/dist/BatchCodec.cjs.cjs +141 -0
  3. package/dist/BatchCodec.d.ts +59 -0
  4. package/dist/BatchCodec.d.ts.map +1 -0
  5. package/dist/BatchCodec.es.js +136 -0
  6. package/dist/CompressionCodec.cjs.cjs +137 -0
  7. package/dist/CompressionCodec.d.ts +88 -0
  8. package/dist/CompressionCodec.d.ts.map +1 -0
  9. package/dist/CompressionCodec.es.js +132 -0
  10. package/dist/EncryptionCodec.cjs.cjs +91 -0
  11. package/dist/EncryptionCodec.d.ts +40 -0
  12. package/dist/EncryptionCodec.d.ts.map +1 -0
  13. package/dist/EncryptionCodec.es.js +85 -0
  14. package/dist/NetworkBinaryStream.cjs.cjs +5 -6
  15. package/dist/NetworkBinaryStream.d.ts +1 -1
  16. package/dist/NetworkBinaryStream.d.ts.map +1 -1
  17. package/dist/NetworkBinaryStream.es.js +3 -4
  18. package/dist/NetworkPacket.cjs.cjs +1 -2
  19. package/dist/NetworkPacket.d.ts +1 -1
  20. package/dist/NetworkPacket.d.ts.map +1 -1
  21. package/dist/NetworkPacket.es.js +1 -2
  22. package/dist/NetworkStructure.cjs.cjs +1 -2
  23. package/dist/NetworkStructure.d.ts +1 -1
  24. package/dist/NetworkStructure.d.ts.map +1 -1
  25. package/dist/NetworkStructure.es.js +1 -2
  26. package/dist/PacketIdentifier.cjs.cjs +12 -3
  27. package/dist/PacketIdentifier.d.ts +11 -1
  28. package/dist/PacketIdentifier.d.ts.map +1 -1
  29. package/dist/PacketIdentifier.es.js +12 -3
  30. package/dist/_virtual/_rolldown/runtime.cjs.cjs +1 -2
  31. package/dist/_virtual/_rolldown/runtime.es.js +1 -2
  32. package/dist/index.cjs.cjs +62 -2
  33. package/dist/index.d.ts +12 -4
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.es.js +26 -3
  36. package/dist/packet/ChunkRadiusUpdatedPacket.cjs.cjs +23 -0
  37. package/dist/packet/ChunkRadiusUpdatedPacket.d.ts +13 -0
  38. package/dist/packet/ChunkRadiusUpdatedPacket.d.ts.map +1 -0
  39. package/dist/packet/ChunkRadiusUpdatedPacket.es.js +18 -0
  40. package/dist/packet/ClientToServerHandshakePacket.cjs.cjs +27 -0
  41. package/dist/packet/ClientToServerHandshakePacket.d.ts +16 -0
  42. package/dist/packet/ClientToServerHandshakePacket.d.ts.map +1 -0
  43. package/dist/packet/ClientToServerHandshakePacket.es.js +22 -0
  44. package/dist/packet/DisconnectPacket.cjs.cjs +56 -0
  45. package/dist/packet/DisconnectPacket.d.ts +25 -0
  46. package/dist/packet/DisconnectPacket.d.ts.map +1 -0
  47. package/dist/packet/DisconnectPacket.es.js +51 -0
  48. package/dist/packet/ItemRegistryPacket.cjs.cjs +53 -0
  49. package/dist/packet/ItemRegistryPacket.d.ts +34 -0
  50. package/dist/packet/ItemRegistryPacket.d.ts.map +1 -0
  51. package/dist/packet/ItemRegistryPacket.es.js +48 -0
  52. package/dist/packet/LevelChunkPacket.cjs.cjs +60 -0
  53. package/dist/packet/LevelChunkPacket.d.ts +32 -0
  54. package/dist/packet/LevelChunkPacket.d.ts.map +1 -0
  55. package/dist/packet/LevelChunkPacket.es.js +55 -0
  56. package/dist/packet/LoginPacket.cjs.cjs +47 -0
  57. package/dist/packet/LoginPacket.d.ts +29 -0
  58. package/dist/packet/LoginPacket.d.ts.map +1 -0
  59. package/dist/packet/LoginPacket.es.js +42 -0
  60. package/dist/packet/MovePlayerPacket.cjs.cjs +76 -0
  61. package/dist/packet/MovePlayerPacket.d.ts +44 -0
  62. package/dist/packet/MovePlayerPacket.d.ts.map +1 -0
  63. package/dist/packet/MovePlayerPacket.es.js +70 -0
  64. package/dist/packet/NetworkSettingsPacket.cjs.cjs +36 -0
  65. package/dist/packet/NetworkSettingsPacket.d.ts +24 -0
  66. package/dist/packet/NetworkSettingsPacket.d.ts.map +1 -0
  67. package/dist/packet/NetworkSettingsPacket.es.js +31 -0
  68. package/dist/packet/PlayStatusPacket.cjs.cjs +42 -0
  69. package/dist/packet/PlayStatusPacket.d.ts +29 -0
  70. package/dist/packet/PlayStatusPacket.d.ts.map +1 -0
  71. package/dist/packet/PlayStatusPacket.es.js +36 -0
  72. package/dist/packet/PlayerActionPacket.cjs.cjs +79 -0
  73. package/dist/packet/PlayerActionPacket.d.ts +63 -0
  74. package/dist/packet/PlayerActionPacket.d.ts.map +1 -0
  75. package/dist/packet/PlayerActionPacket.es.js +73 -0
  76. package/dist/packet/RequestChunkRadiusPacket.cjs.cjs +33 -0
  77. package/dist/packet/RequestChunkRadiusPacket.d.ts +21 -0
  78. package/dist/packet/RequestChunkRadiusPacket.d.ts.map +1 -0
  79. package/dist/packet/RequestChunkRadiusPacket.es.js +28 -0
  80. package/dist/packet/RequestNetworkSettingsPacket.cjs.cjs +30 -0
  81. package/dist/packet/RequestNetworkSettingsPacket.d.ts +19 -0
  82. package/dist/packet/RequestNetworkSettingsPacket.d.ts.map +1 -0
  83. package/dist/packet/RequestNetworkSettingsPacket.es.js +25 -0
  84. package/dist/packet/ResourcePackResponsePacket.cjs.cjs +77 -0
  85. package/dist/packet/ResourcePackResponsePacket.d.ts +33 -0
  86. package/dist/packet/ResourcePackResponsePacket.d.ts.map +1 -0
  87. package/dist/packet/ResourcePackResponsePacket.es.js +71 -0
  88. package/dist/packet/ResourcePackStackPacket.cjs.cjs +70 -0
  89. package/dist/packet/ResourcePackStackPacket.d.ts +34 -0
  90. package/dist/packet/ResourcePackStackPacket.d.ts.map +1 -0
  91. package/dist/packet/ResourcePackStackPacket.es.js +65 -0
  92. package/dist/packet/ResourcePacksInfoPacket.cjs.cjs +87 -0
  93. package/dist/packet/ResourcePacksInfoPacket.d.ts +49 -0
  94. package/dist/packet/ResourcePacksInfoPacket.d.ts.map +1 -0
  95. package/dist/packet/ResourcePacksInfoPacket.es.js +82 -0
  96. package/dist/packet/ServerToClientHandshakePacket.cjs.cjs +29 -0
  97. package/dist/packet/ServerToClientHandshakePacket.d.ts +22 -0
  98. package/dist/packet/ServerToClientHandshakePacket.d.ts.map +1 -0
  99. package/dist/packet/ServerToClientHandshakePacket.es.js +24 -0
  100. package/dist/packet/SetLocalPlayerAsInitializedPacket.cjs.cjs +28 -0
  101. package/dist/packet/SetLocalPlayerAsInitializedPacket.d.ts +17 -0
  102. package/dist/packet/SetLocalPlayerAsInitializedPacket.d.ts.map +1 -0
  103. package/dist/packet/SetLocalPlayerAsInitializedPacket.es.js +23 -0
  104. package/dist/packet/StartGamePacket.cjs.cjs +449 -0
  105. package/dist/packet/StartGamePacket.d.ts +183 -0
  106. package/dist/packet/StartGamePacket.d.ts.map +1 -0
  107. package/dist/packet/StartGamePacket.es.js +442 -0
  108. package/dist/packet/TextPacket.cjs.cjs +85 -0
  109. package/dist/packet/TextPacket.d.ts +40 -0
  110. package/dist/packet/TextPacket.d.ts.map +1 -0
  111. package/dist/packet/TextPacket.es.js +79 -0
  112. package/dist/packet/index.cjs.cjs +81 -0
  113. package/dist/packet/index.d.ts +45 -0
  114. package/dist/packet/index.d.ts.map +1 -0
  115. package/dist/packet/index.es.js +50 -0
  116. package/dist/structure/ActorLink.cjs.cjs +2 -3
  117. package/dist/structure/ActorLink.d.ts +2 -1
  118. package/dist/structure/ActorLink.d.ts.map +1 -1
  119. package/dist/structure/ActorLink.es.js +2 -3
  120. package/dist/structure/BlockPos.cjs.cjs +2 -3
  121. package/dist/structure/BlockPos.d.ts +2 -2
  122. package/dist/structure/BlockPos.d.ts.map +1 -1
  123. package/dist/structure/BlockPos.es.js +2 -3
  124. package/dist/structure/ChunkPos.cjs.cjs +2 -3
  125. package/dist/structure/ChunkPos.d.ts +2 -1
  126. package/dist/structure/ChunkPos.d.ts.map +1 -1
  127. package/dist/structure/ChunkPos.es.js +2 -3
  128. package/dist/structure/DataItem.cjs.cjs +3 -4
  129. package/dist/structure/DataItem.d.ts +4 -1
  130. package/dist/structure/DataItem.d.ts.map +1 -1
  131. package/dist/structure/DataItem.es.js +3 -4
  132. package/dist/structure/Experiments.cjs.cjs +2 -3
  133. package/dist/structure/Experiments.d.ts +3 -2
  134. package/dist/structure/Experiments.d.ts.map +1 -1
  135. package/dist/structure/Experiments.es.js +2 -3
  136. package/dist/structure/ItemData.cjs.cjs +2 -3
  137. package/dist/structure/ItemData.d.ts +2 -1
  138. package/dist/structure/ItemData.d.ts.map +1 -1
  139. package/dist/structure/ItemData.es.js +2 -3
  140. package/dist/structure/ItemInstanceUserData.cjs.cjs +2 -3
  141. package/dist/structure/ItemInstanceUserData.d.ts +2 -1
  142. package/dist/structure/ItemInstanceUserData.d.ts.map +1 -1
  143. package/dist/structure/ItemInstanceUserData.es.js +2 -3
  144. package/dist/structure/LevelSettings.cjs.cjs +2 -3
  145. package/dist/structure/LevelSettings.d.ts +6 -3
  146. package/dist/structure/LevelSettings.d.ts.map +1 -1
  147. package/dist/structure/LevelSettings.es.js +2 -3
  148. package/dist/structure/NetworkItemStackDescriptor.cjs.cjs +26 -21
  149. package/dist/structure/NetworkItemStackDescriptor.d.ts +11 -1
  150. package/dist/structure/NetworkItemStackDescriptor.d.ts.map +1 -1
  151. package/dist/structure/NetworkItemStackDescriptor.es.js +26 -21
  152. package/dist/structure/PropertySyncData.cjs.cjs +2 -3
  153. package/dist/structure/PropertySyncData.d.ts +2 -1
  154. package/dist/structure/PropertySyncData.d.ts.map +1 -1
  155. package/dist/structure/PropertySyncData.es.js +2 -3
  156. package/dist/structure/SerializedAbilitiesData.cjs.cjs +2 -3
  157. package/dist/structure/SerializedAbilitiesData.d.ts +4 -3
  158. package/dist/structure/SerializedAbilitiesData.d.ts.map +1 -1
  159. package/dist/structure/SerializedAbilitiesData.es.js +2 -3
  160. package/dist/structure/SerializedAbilityLayer.cjs.cjs +2 -3
  161. package/dist/structure/SerializedAbilityLayer.d.ts +2 -1
  162. package/dist/structure/SerializedAbilityLayer.d.ts.map +1 -1
  163. package/dist/structure/SerializedAbilityLayer.es.js +2 -3
  164. package/dist/structure/SpawnSettings.cjs.cjs +2 -3
  165. package/dist/structure/SpawnSettings.d.ts +4 -2
  166. package/dist/structure/SpawnSettings.d.ts.map +1 -1
  167. package/dist/structure/SpawnSettings.es.js +2 -3
  168. package/dist/structure/SyncedPlayerMovementSettings.cjs.cjs +2 -3
  169. package/dist/structure/SyncedPlayerMovementSettings.d.ts +3 -2
  170. package/dist/structure/SyncedPlayerMovementSettings.d.ts.map +1 -1
  171. package/dist/structure/SyncedPlayerMovementSettings.es.js +2 -3
  172. package/dist/structure/UUID.cjs.cjs +2 -3
  173. package/dist/structure/UUID.d.ts +2 -1
  174. package/dist/structure/UUID.d.ts.map +1 -1
  175. package/dist/structure/UUID.es.js +2 -3
  176. package/dist/structure/Vec2.cjs.cjs +2 -3
  177. package/dist/structure/Vec2.d.ts +2 -2
  178. package/dist/structure/Vec2.d.ts.map +1 -1
  179. package/dist/structure/Vec2.es.js +2 -3
  180. package/dist/structure/Vec3.cjs.cjs +2 -3
  181. package/dist/structure/Vec3.d.ts +2 -2
  182. package/dist/structure/Vec3.d.ts.map +1 -1
  183. package/dist/structure/Vec3.es.js +2 -3
  184. package/dist/structure/index.cjs.cjs +3 -4
  185. package/dist/structure/index.d.ts +15 -15
  186. package/dist/structure/index.es.js +3 -4
  187. package/package.json +7 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,126 @@
1
1
  # @jsprismarine/protocol
2
2
 
3
+ ## 0.15.0-unstable-20261024032303
4
+
5
+ ### Minor Changes
6
+
7
+ - [#2577](https://github.com/JSPrismarine/JSPrismarine/pull/2577) [`74722e8`](https://github.com/JSPrismarine/JSPrismarine/commit/74722e8b07e5865ca42b04ff81503f2a0559bd0b) Thanks [@filiphsps](https://github.com/filiphsps)! - Add the batch layer to `@jsprismarine/protocol`, and a socketless RakNet pair to `@jsprismarine/raknet`.
8
+
9
+ **`BatchCodec` and `CompressionCodec`.** The outermost Minecraft layer - `0xFE`, an optional algorithm byte, then length-prefixed packets - as something both sides of a connection can use, and which knows nothing about what a packet *is*. A batch is a list of opaque buffers, which is what lets it serve a client, a server and a proxy alike.
10
+
11
+ The distinction it draws that the server's `BatchPacket` does not: the algorithm byte is present if and only if compression has been *negotiated*, which is not the same as this batch being compressed. `RequestNetworkSettings` and `NetworkSettings` carry no prefix; from the next packet onward every batch carries one, and a peer may set it to `0xff` whenever compressing would not pay. Those are two codecs - `BatchCodec.uncompressed()` and `BatchCodec.compressed()` - rather than one boolean.
12
+
13
+ Three things fall out of writing it as a codec rather than a packet:
14
+
15
+ - **A compression threshold**, so a small payload goes out uncompressed and says so, instead of deflate reliably making it larger. Mojang's threshold of 0 means "never compress"; read as a plain `byteLength >= threshold` it would have meant the exact opposite.
16
+ - **Bounded inflation.** A batch arrives inside one RakNet message, so the compressed bytes are bounded at roughly 2.8 MiB by reassembly - what they *expand* to was bounded by nothing, and both a client and a server read compressed bytes from a peer they have not authenticated. zlib now enforces a ceiling itself.
17
+ - **Strict splitting.** Every declared length is checked against what actually remains, and the error says at which offset the batch went wrong. An empty batch yields no packets: the `do/while` this replaces always ran once and read a varint out of nothing.
18
+
19
+ `BatchCodecInterop.test.ts` in `@jsprismarine/prismarine` pins the two implementations together, byte for byte in both directions. They will not be swapped for one another in a single commit - the client is built on the codec while the server still runs `BatchPacket` - so for as long as both exist, a disagreement between them is a client that cannot talk to this server.
20
+
21
+ **`LoopbackSocket`.** A connected RakNet pair with no sockets under it, for running a server in the same process as its client. The server keeps talking to a `RakNetSession` and cannot tell it is not on a wire.
22
+
23
+ Framing, ordering, acknowledgement, reassembly and the connected handshake are the ordinary `Session`, unmodified - the alternative, handing frames across directly, would have meant reimplementing `Session.handlePacket`, and single player would then run *similar* code to multiplayer rather than the same code. What is skipped is the offline exchange, which exists only to discover a path MTU and a guid across a network that is not there.
24
+
25
+ Two details it has to get right: delivery goes through a microtask, so a packet answered inline does not nest a second delivery inside the first; and the MTU is 8 KiB rather than "as large as possible", because `Frame` writes its content length *in bits* into an unsigned 16 bit field and anything past 8191 bytes silently wraps it. That is still five and a half times what a real connection negotiates, so a chunk batch that fragments seventeen ways on the wire fragments three ways here.
26
+
27
+ `Loopback.test.ts` runs the same five scenarios twice, once over real sockets and once in memory, and asserts the two agree.
28
+
29
+ - [#2577](https://github.com/JSPrismarine/JSPrismarine/pull/2577) [`5cd4d0a`](https://github.com/JSPrismarine/JSPrismarine/commit/5cd4d0aaf1b101135135157e9f11c55c4c73a5f9) Thanks [@filiphsps](https://github.com/filiphsps)! - A load-testing harness: N synthetic players against a server, and the numbers they produce.
30
+
31
+ `Fleet` connects a fleet at a configured rate, ticks each bot's behaviours at the server's own twenty per second, and reports what happened - login latency percentiles, RakNet round trip, bytes and packets per direction, how many of the requested bots actually got in, and what each refusal was. A bot that fails to connect is recorded and left out rather than aborting the run, because "how many of five hundred got in" is usually the question being asked.
32
+
33
+ Everything random comes from one seed, derived per bot rather than shared. `Math.random` cannot be seeded, and a run that cannot be repeated is not a measurement: two runs against the same server have to differ because the *server* behaved differently. A single generator handed to five hundred bots would have them take turns drawing from one sequence, so what each did would depend on how the others were scheduled - which is the non-determinism the seed exists to remove.
34
+
35
+ Four behaviours, deliberately separable, because a load test is a question and the question is usually about one kind of traffic: `walk` (chunk streaming and entity tracking), `look` (the broadcast path, without touching streaming), `chat` (fan-out - two hundred bots speaking once each is forty thousand deliveries), and `break` (the three-step action sequence a real client sends).
36
+
37
+ It lives in `packages/client/src/bot/` rather than in a package of its own: it is under a thousand lines, it depends only on the client, and nothing will ever depend on it.
38
+
39
+ **A command line, `jsp-client`**, in the same package and for the same reason - the command line is a way of driving the client, not a separate product.
40
+
41
+ ```
42
+ jsp-client bot --host <address> --count <n> [--behaviour walk,chat,break]
43
+ [--duration 10m] [--ramp 5/s] [--seed 1234] [--view-distance 4]
44
+ [--report report.json]
45
+ jsp-client connect --host <address> [--name Steve]
46
+ jsp-client ping --host <address>
47
+ ```
48
+
49
+ Built on `node:util.parseArgs` rather than a dependency: the repository has no argument library anywhere, Node 21 is already the engine requirement, and a binary that has to stay `bun build --compile`-able has every reason not to grow a dependency tree for five flags. A bare `--duration 60` means sixty *seconds*, because the alternative reading wastes an afternoon. It exits 2 for a command line it cannot read and 1 when any bot failed to get in, so it works as a CI gate and not only as something to read.
50
+
51
+ **Five more packets migrated to `NetworkPacket<T>`** - `MovePlayer`, `PlayerAction`, `RequestChunkRadius`, `ChunkRadiusUpdated` and `LevelChunk` - each pinned against the server's own class byte for byte. Two carry traps worth naming: `MovePlayer`'s length depends on one of its own bytes, since the teleport fields exist only for a teleport, and `PlayerAction` writes its block positions with an *unsigned* y, so writing all three signed encodes every height above 63 one byte differently and the reader takes the block face out of the middle of a coordinate. `LevelChunk` reads its envelope and hands the terrain over unparsed, which is what lets a bot skip the expensive half of the packet that makes up most of a server's output.
52
+
53
+ Two more bugs the real binary found that the tests could not:
54
+
55
+ - **Nothing kept the process alive.** Every handle the client owns is `unref`ed so a library never holds its host process open, which in a process whose only job *is* that client leaves nothing holding the event loop. Node exited between the first await and the answer: `ping` printed nothing, the fleet connected nobody, and both returned zero. Invisible under a test runner, which keeps the loop alive by itself.
56
+ - **The report's round-trip time was always `-`.** A report is produced after the run ends, and the RTT lived only on a session that had already been torn down, so the number no report ever saw was the one the harness existed to measure. The transport keeps the last live measurement now.
57
+
58
+ **`ClientSocket.kill` closed its socket one tick too early.** `disconnect` queues a DISCONNECTION_NOTIFICATION and `dgram.send` is asynchronous however immediate the priority was, so closing the handle in the same tick dropped the datagram and the goodbye was never sent. The server then held the session until it timed out - ten seconds counting against the player limit - and a fleet that reconnected found the server full of clients that had already left. Found by the harness itself, on the fifth fleet of a test run.
59
+
60
+ - [#2577](https://github.com/JSPrismarine/JSPrismarine/pull/2577) [`5cd4d0a`](https://github.com/JSPrismarine/JSPrismarine/commit/5cd4d0aaf1b101135135157e9f11c55c4c73a5f9) Thanks [@filiphsps](https://github.com/filiphsps)! - The client can log in. `@jsprismarine/client` connects to a Bedrock server, completes the handshake and stands in the world - proved by a test that boots a real `Server` in-process and joins it with nothing mocked in between.
61
+
62
+ **`@jsprismarine/auth`, new.** The login identity: the JWT chain, the client data blob, and the P-384 key both are signed with. A package of its own because the proxy shares it - it needs to *verify* an incoming chain when acting as a server in online mode, which a client never does. A provider is chosen at the edge of the application, so which one is in play is a deployment decision rather than a build-time one. `OfflineAuthProvider` signs the single self-signed chain link a real client sends when it is not signed in, and generates its key per instance, because the key *is* the identity: five hundred bots under one name still need five hundred of them.
63
+
64
+ The default skin is generated from flat colour blocks laid over the regions the classic humanoid geometry samples. Mojang's textures cannot be redistributed and a client that refused to log in without one would be useless. It is also not optional in the way it looks: the server builds a `Skin` out of the client data and dereferences eighteen fields without checking any of them, so a client that omits one crashes the login handler rather than being refused.
65
+
66
+ **Ten packets migrated to `NetworkPacket<T>`** - the ones a join cannot do without. `Login` is among them, and is encodable for the first time: `LoginPacket.encodePayload` had been a commented-out block with a hardcoded chain in it since it was written. `StartGame` is deliberately partial, reading the fields a client cannot function without and stopping; the rest is positional and would have to be modelled in full before any of it could be read. Stopping early is safe because a batch length-delimits each packet.
67
+
68
+ **`@jsprismarine/client`, rebuilt.** It was a 230-line proof of concept that could not finish a handshake - it hardcoded RakNet protocol 10 against this repository's own 11, drove a *server* session by passing itself as `this as any`, and sent a Login that encoded nothing. It is now a transport seam, a packet registry with no `Server` in the signature, a batching session and a login state machine, and it depends on `@jsprismarine/protocol` rather than on the server.
69
+
70
+ Three bugs found by pointing it at a real server:
71
+
72
+ - **Inbound batches were dispatched out of order.** Decoding is asynchronous because inflating runs on libuv's thread pool, so a batch takes as long as its *size* says, and a large one was overtaken by every small one that arrived while it was still inflating. A 37 KiB `StartGame` surfacing after the `PlayStatus` that was meant to follow it is not a slow `StartGame`, it is a missing one - the login failed with "the server spawned us without ever sending StartGame" while the packet was still inflating. Inbound is now a chain, the receiving mirror of the one `MinecraftSession` already kept on the way out.
73
+ - **`ResourcePackResponsePacket.encodePayload` could not be read by its own `decodePayload`**: a spare `writeUnsignedShortLE(0)` sat in front of the count. Latent while only the server handled this packet, which decodes and never writes one.
74
+ - **`World.getPlayerData` raised "Player has no XUID"** for every unauthenticated player - a case `LoginHandler` explicitly permits, `savePlayerData` has always written a file for, and the very next line already had a fallback for. It logged as an error on every offline join. Reader and writer now share one key.
75
+
76
+ - [#2577](https://github.com/JSPrismarine/JSPrismarine/pull/2577) [`aeaf717`](https://github.com/JSPrismarine/JSPrismarine/commit/aeaf7171f271b7c6b03df42a6152d1f63b82826b) Thanks [@filiphsps](https://github.com/filiphsps)! - Protocol 2193: Minecraft 1.26.50 and 1.26.51.
77
+
78
+ Everything that was captured for 1.26.40 was captured again from a Bedrock Dedicated Server 1.26.51.1 - the item table (2076 items), the block palette (1477 blocks, 22079 states: 98 new wool and concrete stairs and slabs, a `minecraft:corner` on every stair, four connection states on every fence, pane and bar) and the recipes (1967) - and three things a 2193 client will not get into the world without were captured for the first time: the jigsaw structure rules, sent as `JigsawStructureDataPacket` before `StartGame` because the client disconnects with `MissingStructureData` otherwise; the definitions of the ninety-eight vanilla blocks that 1.26.50 defines in data, which `StartGamePacket` now declares the way a real server does; and the biome definitions, whose packet stopped being two loose lists and became a map of biome data plus a shared string table - a client reaches the world-generation screen with an empty one and gives up, so the real payload is now replayed. `packages/client/tools/capture-server-data.mjs` records all of them in one login.
79
+
80
+ This was found the only way it could be: a retail 1.26.51 client reaching the world-generation screen and stalling. The same client also confirmed that, faced with a server that answers NetherNet's `GET /v1/join` signaling probe with a 404, it falls back to a direct RakNet connection - which is the transport this server speaks.
81
+
82
+ The `LevelChunkPacket` payload wrote its biome sections with the wrong palette header. Each biome sub chunk is a paletted store like the block ones, and its header is `(bitsPerBlock << 1) | networkFlag`; the server wrote `bitsPerBlock 0` but left the network flag clear (`0` instead of `1`), which tells the client the palette is the *persistent* on-disk kind whose entries are NBT compounds. The client then reads the biome value varint as the start of a tag, the whole column decodes to nonsense, and it drops the join at world generation - the last thing standing between a real client and spawning. The section is now `01` (single value, network flag set) plus the biome value per sub chunk, one sub chunk for every one of the client's twenty-four, then the border byte, checked by decoding the packet the server sends and confirming it is consumed to the last byte. This too was invisible to the server's own client, which never deserialized terrain.
83
+
84
+ **The packets the client actually sends were wrong at 2168, and are right now.** The 2168 work was checked with this repository's own client, which reads `StartGame` partially, moves with `MovePlayerPacket` and never touches an inventory - so nothing exercised the paths a real client takes, and several of those had drifted years earlier:
85
+
86
+ - `StartGamePacket` still wrote a movement mode in front of the movement settings. That field left the protocol at 1.21.90, when client authoritative movement was retired, and every field after it went out one byte late. The full layout is now modelled in `@jsprismarine/protocol` and checked by decoding the packet BDS 1.26.51 sends and writing it back byte for byte.
87
+ - A real client moves with `PlayerAuthInputPacket`, every tick, and has since 1.21.80; the server had no decoder for it, so a real player could not move, and neither could they break a block, because a survival break rides in that packet's block actions. Both are read now and go through the same code as `MovePlayer` and `PlayerAction` do.
88
+ - `InventoryTransactionPacket` was read in its 748 layout: no presence byte before the legacy slot list, window ids and flags inferred rather than read, and no `Hand`, which 2193 added. `ItemStackRequestPacket` likewise: an action's selector, the four fixed bytes of a stack net id, the client's own descriptor form for auto-craft ingredients. `ItemStackResponsePacket` wrote none of its three optionals. All three follow gophertunnel's 2193 encoding now, with tests that write the bytes by hand.
89
+ - Block positions in `PlayerActionPacket` were read with an unsigned y, the layout up to 1.26.0; a block below sea level came out in the billions.
90
+
91
+ `PlaySoundPacket` gained the loop count, the range bypass and the two optionals 1.26.50 added, and can be encoded at all. `BuildPlatform` lost its retired members, `UWP` among them: BDS validates a login against the list now and refuses the value the client used to report, so it reports `WIN32`.
92
+
93
+ **The datagen reads Mojang's new publications.** `enums.html` is gone; a release is a `metadata.zip` of JSON schemas attached to a GitHub release, and the schemas list an enum's members without their values. `import-schemas.js` pins two releases - the target for the members, the first later preview whose schemas carry `x-enum-binary-value` for the numbers - into `snapshot/enums.json`, which `generate-enums.js` prefers over the old HTML dump wherever the schemas describe an enum. `DisconnectReason` and `PlayerAuthInputData` are generated from them. The recipe generator reads the `.brarchive` files a 1.26.50 server packs its behaviour packs into, and the palette capture places every block in four settings so that none pops before the save.
94
+
95
+ One thing this release cannot fix: BDS 1.26.51 defaults to `transport=nethernet` and prints that RakNet is no longer a supported transport for players. This server speaks RakNet only. Whether a 1.26.51 client still connects to a RakNet server by address is not something this repository can test.
96
+
97
+ ### Patch Changes
98
+
99
+ - [#2577](https://github.com/JSPrismarine/JSPrismarine/pull/2577) [`6b60a88`](https://github.com/JSPrismarine/JSPrismarine/commit/6b60a88e40ad9751ea177f4273779869486de7e0) Thanks [@filiphsps](https://github.com/filiphsps)! - Move JSBinaryUtils into the monorepo as `@jsprismarine/binaryutils`.
100
+
101
+ It was developed in its own repository and consumed here as a pinned npm dependency, which meant a
102
+ change to the binary stream needed a release there before it could be used here. It now lives in
103
+ `packages/binaryutils` and every consumer resolves it through the workspace, so it builds, typechecks
104
+ and tests together with the packages that use it.
105
+
106
+ The published name changes: `@jsprismarine/jsbinaryutils` is deprecated on npm and its last release
107
+ re-exports this package, so existing installs keep working. The API is unchanged.
108
+
109
+ Two things came along with the jump from the pinned 5.5.3 to current: `write()` accepted a
110
+ `Uint8Array` in its signature but called `Buffer#copy` on it, so anything not already a `Buffer`
111
+ threw at runtime — it now uses `set`. And the single internal `buffer` field is split into a read and
112
+ a write buffer, so code reaching past the public API to assign `.buffer` no longer populates the read
113
+ side; use `setReadBuffer()`.
114
+
115
+ `@jsprismarine/protocol` also gained the dependency in its manifest — it imported the package while
116
+ relying on it being hoisted into the root `node_modules`.
117
+
118
+ - [#2579](https://github.com/JSPrismarine/JSPrismarine/pull/2579) [`ff8648e`](https://github.com/JSPrismarine/JSPrismarine/commit/ff8648e6f92e87f702a1fefc09d306db3307da8a) Thanks [@filiphsps](https://github.com/filiphsps)! - Build declaration files with TypeScript 7 through `tsc-ts`. JSON data exports are now typed structurally (`string`, `number`) instead of as literal values.
119
+ - Updated dependencies [[`6b60a88`](https://github.com/JSPrismarine/JSPrismarine/commit/6b60a88e40ad9751ea177f4273779869486de7e0), [`b6c3d73`](https://github.com/JSPrismarine/JSPrismarine/commit/b6c3d73168fa0397205e20fcbf3cc19c4d0bac36), [`60e9e6f`](https://github.com/JSPrismarine/JSPrismarine/commit/60e9e6f981cce46edf2bb2ee0b7b1eea3b7360ee), [`aeaf717`](https://github.com/JSPrismarine/JSPrismarine/commit/aeaf7171f271b7c6b03df42a6152d1f63b82826b), [`ff8648e`](https://github.com/JSPrismarine/JSPrismarine/commit/ff8648e6f92e87f702a1fefc09d306db3307da8a)]:
120
+ - @jsprismarine/binaryutils@0.15.0-unstable-20261024032303
121
+ - @jsprismarine/nbt@0.15.0-unstable-20261024032303
122
+ - @jsprismarine/minecraft@0.15.0-unstable-20261024032303
123
+
3
124
  ## 0.14.2
4
125
 
5
126
  ### Patch Changes
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const require_runtime = require("./_virtual/_rolldown/runtime.cjs.cjs");
4
+ const require_CompressionCodec = require("./CompressionCodec.cjs.cjs");
5
+ let _jsprismarine_binaryutils = require("@jsprismarine/binaryutils");
6
+ _jsprismarine_binaryutils = require_runtime.__toESM(_jsprismarine_binaryutils, 1);
7
+ let _jsprismarine_minecraft = require("@jsprismarine/minecraft");
8
+ // src/BatchCodec.ts
9
+ /** The id every batch carries, and the only packet id RakNet ever sees from Minecraft. */
10
+ var BATCH_PACKET_ID = 254;
11
+ /**
12
+ * A batch: the outermost Minecraft layer, sitting directly inside a RakNet frame.
13
+ *
14
+ * ```
15
+ * 0xFE │ [algorithm byte] │ ⟨varint len, packet⟩ ⟨varint len, packet⟩ …
16
+ * ```
17
+ *
18
+ * The algorithm byte is present if and only if compression has been *negotiated* - which is
19
+ * not the same as this batch being compressed, and conflating the two is the trap here. A
20
+ * connection sends `RequestNetworkSettings` and receives `NetworkSettings` with no prefix at
21
+ * all; from the packet after that one, every batch carries a prefix, and a peer is free to
22
+ * make it {@link UNCOMPRESSED_BATCH} whenever compressing would not pay. So the two states
23
+ * are two codecs - {@link BatchCodec.uncompressed} and {@link BatchCodec.compressed} - and a
24
+ * connection replaces one with the other exactly once.
25
+ *
26
+ * Nothing here knows what a packet *is*: a batch is a list of opaque buffers. That is what
27
+ * lets a proxy route on ids it has no definition for, and what keeps this usable from both
28
+ * sides of a connection.
29
+ */
30
+ var BatchCodec = class BatchCodec {
31
+ compression;
32
+ constructor(compression) {
33
+ this.compression = compression;
34
+ }
35
+ /**
36
+ * The pre-negotiation form, carrying no algorithm byte. Only `RequestNetworkSettings`
37
+ * and `NetworkSettings` itself travel this way.
38
+ */
39
+ static uncompressed() {
40
+ return new BatchCodec(null);
41
+ }
42
+ /** The post-negotiation form: every batch carries an algorithm byte. */
43
+ static compressed(settings) {
44
+ return new BatchCodec(settings instanceof require_CompressionCodec.CompressionCodec ? settings : new require_CompressionCodec.CompressionCodec(settings));
45
+ }
46
+ /** Whether compression has been negotiated, i.e. whether batches carry a prefix byte. */
47
+ isCompressionEnabled() {
48
+ return this.compression !== null;
49
+ }
50
+ encode(packets) {
51
+ const payload = BatchCodec.joinPackets(packets);
52
+ if (this.compression === null) return Buffer.concat([Buffer.from([254]), payload]);
53
+ const algorithm = this.compression.algorithmFor(payload.byteLength);
54
+ return BatchCodec.frame(algorithm, this.compression.compress(payload));
55
+ }
56
+ /** The same, with the deflate handed to zlib's asynchronous API. */
57
+ async encodeAsync(packets) {
58
+ const payload = BatchCodec.joinPackets(packets);
59
+ if (this.compression === null) return Buffer.concat([Buffer.from([254]), payload]);
60
+ const algorithm = this.compression.algorithmFor(payload.byteLength);
61
+ return BatchCodec.frame(algorithm, await this.compression.compressAsync(payload));
62
+ }
63
+ decode(batch) {
64
+ const { algorithm, body } = this.split(batch);
65
+ return BatchCodec.splitPackets(this.compression === null ? body : this.compression.decompress(algorithm, body));
66
+ }
67
+ /** The same, with the inflate handed to zlib's asynchronous API. */
68
+ async decodeAsync(batch) {
69
+ const { algorithm, body } = this.split(batch);
70
+ return BatchCodec.splitPackets(this.compression === null ? body : await this.compression.decompressAsync(algorithm, body));
71
+ }
72
+ static frame(algorithm, body) {
73
+ return Buffer.concat([Buffer.from([254, require_CompressionCodec.CompressionCodec.toPrefix(algorithm)]), body]);
74
+ }
75
+ /** Strips the id and, when compression is on, the algorithm byte. */
76
+ split(batch) {
77
+ if (batch.byteLength < 1 || batch[0] !== 254) throw new Error(`Not a batch: expected id 0x${254 .toString(16)}, got 0x${batch[0]?.toString(16)}`);
78
+ if (this.compression === null) return {
79
+ algorithm: _jsprismarine_minecraft.PacketCompressionAlgorithm.NONE,
80
+ body: batch.subarray(1)
81
+ };
82
+ if (batch.byteLength < 2) throw new Error("Truncated batch: compression is negotiated but the algorithm byte is missing");
83
+ return {
84
+ algorithm: require_CompressionCodec.CompressionCodec.fromPrefix(batch[1]),
85
+ body: batch.subarray(2)
86
+ };
87
+ }
88
+ static joinPackets(packets) {
89
+ const stream = new _jsprismarine_binaryutils.default();
90
+ for (const packet of packets) {
91
+ stream.writeUnsignedVarInt(packet.byteLength);
92
+ stream.write(packet);
93
+ }
94
+ return stream.getBuffer();
95
+ }
96
+ /**
97
+ * Splits the decompressed payload back into packets.
98
+ *
99
+ * A manual cursor rather than a `BinaryStream`, for the offsets: every length here is
100
+ * peer-controlled and has to be checked against what is actually left, and saying
101
+ * *where* a batch went wrong is most of the value of noticing. An empty payload yields
102
+ * no packets rather than throwing - the `do/while` this replaced always ran once and
103
+ * tried to read a varint out of nothing.
104
+ */
105
+ static splitPackets(payload) {
106
+ const packets = [];
107
+ let cursor = 0;
108
+ while (cursor < payload.byteLength) {
109
+ const start = cursor;
110
+ const { value: length, next } = BatchCodec.readUnsignedVarInt(payload, cursor);
111
+ cursor = next;
112
+ if (length === 0) throw new Error(`Malformed batch: zero length packet at offset ${start}`);
113
+ if (length > payload.byteLength - cursor) throw new Error(`Malformed batch: packet at offset ${start} declares ${length} bytes, ${payload.byteLength - cursor} remain`);
114
+ packets.push(payload.subarray(cursor, cursor + length));
115
+ cursor += length;
116
+ }
117
+ return packets;
118
+ }
119
+ /** LEB128, matching `BinaryStream.readUnsignedVarInt` including its over-long rejection. */
120
+ static readUnsignedVarInt(buffer, offset) {
121
+ let value = 0;
122
+ let cursor = offset;
123
+ for (let shift = 0; shift <= 28; shift += 7) {
124
+ if (cursor >= buffer.byteLength) throw new Error(`Malformed batch: varint at offset ${offset} runs past the end of the payload`);
125
+ const byte = buffer[cursor++];
126
+ value |= (byte & 127) << shift;
127
+ if ((byte & 128) === 0) {
128
+ if (shift === 28 && (byte & 112) !== 0) throw new Error(`Malformed batch: varint at offset ${offset} overflows 32 bits`);
129
+ return {
130
+ value: value >>> 0,
131
+ next: cursor
132
+ };
133
+ }
134
+ }
135
+ throw new Error(`Malformed batch: varint at offset ${offset} did not terminate after 5 bytes`);
136
+ }
137
+ };
138
+ exports.BATCH_PACKET_ID = BATCH_PACKET_ID;
139
+ exports.BatchCodec = BatchCodec;
140
+
141
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQmF0Y2hDb2RlYy5janMuY2pzIiwibmFtZXMiOlsiQ29tcHJlc3Npb25Db2RlYyIsIlBhY2tldENvbXByZXNzaW9uQWxnb3JpdGhtIiwiQmluYXJ5U3RyZWFtIl0sInNvdXJjZXMiOlsiLi4vc3JjL0JhdGNoQ29kZWMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IEJpbmFyeVN0cmVhbSBmcm9tICdAanNwcmlzbWFyaW5lL2JpbmFyeXV0aWxzJztcbmltcG9ydCB7IFBhY2tldENvbXByZXNzaW9uQWxnb3JpdGhtIH0gZnJvbSAnQGpzcHJpc21hcmluZS9taW5lY3JhZnQnO1xuaW1wb3J0IHR5cGUgeyBDb21wcmVzc2lvblNldHRpbmdzIH0gZnJvbSAnLi9Db21wcmVzc2lvbkNvZGVjJztcbmltcG9ydCB7IENvbXByZXNzaW9uQ29kZWMgfSBmcm9tICcuL0NvbXByZXNzaW9uQ29kZWMnO1xuXG4vKiogVGhlIGlkIGV2ZXJ5IGJhdGNoIGNhcnJpZXMsIGFuZCB0aGUgb25seSBwYWNrZXQgaWQgUmFrTmV0IGV2ZXIgc2VlcyBmcm9tIE1pbmVjcmFmdC4gKi9cbmV4cG9ydCBjb25zdCBCQVRDSF9QQUNLRVRfSUQgPSAweGZlO1xuXG4vKipcbiAqIEEgYmF0Y2g6IHRoZSBvdXRlcm1vc3QgTWluZWNyYWZ0IGxheWVyLCBzaXR0aW5nIGRpcmVjdGx5IGluc2lkZSBhIFJha05ldCBmcmFtZS5cbiAqXG4gKiBgYGBcbiAqIDB4RkUg4pSCIFthbGdvcml0aG0gYnl0ZV0g4pSCIOKfqHZhcmludCBsZW4sIHBhY2tldOKfqSDin6h2YXJpbnQgbGVuLCBwYWNrZXTin6kg4oCmXG4gKiBgYGBcbiAqXG4gKiBUaGUgYWxnb3JpdGhtIGJ5dGUgaXMgcHJlc2VudCBpZiBhbmQgb25seSBpZiBjb21wcmVzc2lvbiBoYXMgYmVlbiAqbmVnb3RpYXRlZCogLSB3aGljaCBpc1xuICogbm90IHRoZSBzYW1lIGFzIHRoaXMgYmF0Y2ggYmVpbmcgY29tcHJlc3NlZCwgYW5kIGNvbmZsYXRpbmcgdGhlIHR3byBpcyB0aGUgdHJhcCBoZXJlLiBBXG4gKiBjb25uZWN0aW9uIHNlbmRzIGBSZXF1ZXN0TmV0d29ya1NldHRpbmdzYCBhbmQgcmVjZWl2ZXMgYE5ldHdvcmtTZXR0aW5nc2Agd2l0aCBubyBwcmVmaXggYXRcbiAqIGFsbDsgZnJvbSB0aGUgcGFja2V0IGFmdGVyIHRoYXQgb25lLCBldmVyeSBiYXRjaCBjYXJyaWVzIGEgcHJlZml4LCBhbmQgYSBwZWVyIGlzIGZyZWUgdG9cbiAqIG1ha2UgaXQge0BsaW5rIFVOQ09NUFJFU1NFRF9CQVRDSH0gd2hlbmV2ZXIgY29tcHJlc3Npbmcgd291bGQgbm90IHBheS4gU28gdGhlIHR3byBzdGF0ZXNcbiAqIGFyZSB0d28gY29kZWNzIC0ge0BsaW5rIEJhdGNoQ29kZWMudW5jb21wcmVzc2VkfSBhbmQge0BsaW5rIEJhdGNoQ29kZWMuY29tcHJlc3NlZH0gLSBhbmQgYVxuICogY29ubmVjdGlvbiByZXBsYWNlcyBvbmUgd2l0aCB0aGUgb3RoZXIgZXhhY3RseSBvbmNlLlxuICpcbiAqIE5vdGhpbmcgaGVyZSBrbm93cyB3aGF0IGEgcGFja2V0ICppcyo6IGEgYmF0Y2ggaXMgYSBsaXN0IG9mIG9wYXF1ZSBidWZmZXJzLiBUaGF0IGlzIHdoYXRcbiAqIGxldHMgYSBwcm94eSByb3V0ZSBvbiBpZHMgaXQgaGFzIG5vIGRlZmluaXRpb24gZm9yLCBhbmQgd2hhdCBrZWVwcyB0aGlzIHVzYWJsZSBmcm9tIGJvdGhcbiAqIHNpZGVzIG9mIGEgY29ubmVjdGlvbi5cbiAqL1xuZXhwb3J0IGNsYXNzIEJhdGNoQ29kZWMge1xuICAgIHByaXZhdGUgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBjb21wcmVzc2lvbjogQ29tcHJlc3Npb25Db2RlYyB8IG51bGwpIHt9XG5cbiAgICAvKipcbiAgICAgKiBUaGUgcHJlLW5lZ290aWF0aW9uIGZvcm0sIGNhcnJ5aW5nIG5vIGFsZ29yaXRobSBieXRlLiBPbmx5IGBSZXF1ZXN0TmV0d29ya1NldHRpbmdzYFxuICAgICAqIGFuZCBgTmV0d29ya1NldHRpbmdzYCBpdHNlbGYgdHJhdmVsIHRoaXMgd2F5LlxuICAgICAqL1xuICAgIHB1YmxpYyBzdGF0aWMgdW5jb21wcmVzc2VkKCk6IEJhdGNoQ29kZWMge1xuICAgICAgICByZXR1cm4gbmV3IEJhdGNoQ29kZWMobnVsbCk7XG4gICAgfVxuXG4gICAgLyoqIFRoZSBwb3N0LW5lZ290aWF0aW9uIGZvcm06IGV2ZXJ5IGJhdGNoIGNhcnJpZXMgYW4gYWxnb3JpdGhtIGJ5dGUuICovXG4gICAgcHVibGljIHN0YXRpYyBjb21wcmVzc2VkKHNldHRpbmdzOiBDb21wcmVzc2lvblNldHRpbmdzIHwgQ29tcHJlc3Npb25Db2RlYyk6IEJhdGNoQ29kZWMge1xuICAgICAgICByZXR1cm4gbmV3IEJhdGNoQ29kZWMoc2V0dGluZ3MgaW5zdGFuY2VvZiBDb21wcmVzc2lvbkNvZGVjID8gc2V0dGluZ3MgOiBuZXcgQ29tcHJlc3Npb25Db2RlYyhzZXR0aW5ncykpO1xuICAgIH1cblxuICAgIC8qKiBXaGV0aGVyIGNvbXByZXNzaW9uIGhhcyBiZWVuIG5lZ290aWF0ZWQsIGkuZS4gd2hldGhlciBiYXRjaGVzIGNhcnJ5IGEgcHJlZml4IGJ5dGUuICovXG4gICAgcHVibGljIGlzQ29tcHJlc3Npb25FbmFibGVkKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdGhpcy5jb21wcmVzc2lvbiAhPT0gbnVsbDtcbiAgICB9XG5cbiAgICBwdWJsaWMgZW5jb2RlKHBhY2tldHM6IHJlYWRvbmx5IEJ1ZmZlcltdKTogQnVmZmVyIHtcbiAgICAgICAgY29uc3QgcGF5bG9hZCA9IEJhdGNoQ29kZWMuam9pblBhY2tldHMocGFja2V0cyk7XG5cbiAgICAgICAgaWYgKHRoaXMuY29tcHJlc3Npb24gPT09IG51bGwpIHtcbiAgICAgICAgICAgIHJldHVybiBCdWZmZXIuY29uY2F0KFtCdWZmZXIuZnJvbShbQkFUQ0hfUEFDS0VUX0lEXSksIHBheWxvYWRdKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGFsZ29yaXRobSA9IHRoaXMuY29tcHJlc3Npb24uYWxnb3JpdGhtRm9yKHBheWxvYWQuYnl0ZUxlbmd0aCk7XG4gICAgICAgIHJldHVybiBCYXRjaENvZGVjLmZyYW1lKGFsZ29yaXRobSwgdGhpcy5jb21wcmVzc2lvbi5jb21wcmVzcyhwYXlsb2FkKSk7XG4gICAgfVxuXG4gICAgLyoqIFRoZSBzYW1lLCB3aXRoIHRoZSBkZWZsYXRlIGhhbmRlZCB0byB6bGliJ3MgYXN5bmNocm9ub3VzIEFQSS4gKi9cbiAgICBwdWJsaWMgYXN5bmMgZW5jb2RlQXN5bmMocGFja2V0czogcmVhZG9ubHkgQnVmZmVyW10pOiBQcm9taXNlPEJ1ZmZlcj4ge1xuICAgICAgICBjb25zdCBwYXlsb2FkID0gQmF0Y2hDb2RlYy5qb2luUGFja2V0cyhwYWNrZXRzKTtcblxuICAgICAgICBpZiAodGhpcy5jb21wcmVzc2lvbiA9PT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuIEJ1ZmZlci5jb25jYXQoW0J1ZmZlci5mcm9tKFtCQVRDSF9QQUNLRVRfSURdKSwgcGF5bG9hZF0pO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgYWxnb3JpdGhtID0gdGhpcy5jb21wcmVzc2lvbi5hbGdvcml0aG1Gb3IocGF5bG9hZC5ieXRlTGVuZ3RoKTtcbiAgICAgICAgcmV0dXJuIEJhdGNoQ29kZWMuZnJhbWUoYWxnb3JpdGhtLCBhd2FpdCB0aGlzLmNvbXByZXNzaW9uLmNvbXByZXNzQXN5bmMocGF5bG9hZCkpO1xuICAgIH1cblxuICAgIHB1YmxpYyBkZWNvZGUoYmF0Y2g6IEJ1ZmZlcik6IEJ1ZmZlcltdIHtcbiAgICAgICAgY29uc3QgeyBhbGdvcml0aG0sIGJvZHkgfSA9IHRoaXMuc3BsaXQoYmF0Y2gpO1xuXG4gICAgICAgIHJldHVybiBCYXRjaENvZGVjLnNwbGl0UGFja2V0cyh0aGlzLmNvbXByZXNzaW9uID09PSBudWxsID8gYm9keSA6IHRoaXMuY29tcHJlc3Npb24uZGVjb21wcmVzcyhhbGdvcml0aG0sIGJvZHkpKTtcbiAgICB9XG5cbiAgICAvKiogVGhlIHNhbWUsIHdpdGggdGhlIGluZmxhdGUgaGFuZGVkIHRvIHpsaWIncyBhc3luY2hyb25vdXMgQVBJLiAqL1xuICAgIHB1YmxpYyBhc3luYyBkZWNvZGVBc3luYyhiYXRjaDogQnVmZmVyKTogUHJvbWlzZTxCdWZmZXJbXT4ge1xuICAgICAgICBjb25zdCB7IGFsZ29yaXRobSwgYm9keSB9ID0gdGhpcy5zcGxpdChiYXRjaCk7XG5cbiAgICAgICAgcmV0dXJuIEJhdGNoQ29kZWMuc3BsaXRQYWNrZXRzKFxuICAgICAgICAgICAgdGhpcy5jb21wcmVzc2lvbiA9PT0gbnVsbCA/IGJvZHkgOiBhd2FpdCB0aGlzLmNvbXByZXNzaW9uLmRlY29tcHJlc3NBc3luYyhhbGdvcml0aG0sIGJvZHkpXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzdGF0aWMgZnJhbWUoYWxnb3JpdGhtOiBQYWNrZXRDb21wcmVzc2lvbkFsZ29yaXRobSwgYm9keTogQnVmZmVyKTogQnVmZmVyIHtcbiAgICAgICAgcmV0dXJuIEJ1ZmZlci5jb25jYXQoW0J1ZmZlci5mcm9tKFtCQVRDSF9QQUNLRVRfSUQsIENvbXByZXNzaW9uQ29kZWMudG9QcmVmaXgoYWxnb3JpdGhtKV0pLCBib2R5XSk7XG4gICAgfVxuXG4gICAgLyoqIFN0cmlwcyB0aGUgaWQgYW5kLCB3aGVuIGNvbXByZXNzaW9uIGlzIG9uLCB0aGUgYWxnb3JpdGhtIGJ5dGUuICovXG4gICAgcHJpdmF0ZSBzcGxpdChiYXRjaDogQnVmZmVyKTogeyBhbGdvcml0aG06IFBhY2tldENvbXByZXNzaW9uQWxnb3JpdGhtOyBib2R5OiBCdWZmZXIgfSB7XG4gICAgICAgIGlmIChiYXRjaC5ieXRlTGVuZ3RoIDwgMSB8fCBiYXRjaFswXSAhPT0gQkFUQ0hfUEFDS0VUX0lEKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAgICAgYE5vdCBhIGJhdGNoOiBleHBlY3RlZCBpZCAweCR7QkFUQ0hfUEFDS0VUX0lELnRvU3RyaW5nKDE2KX0sIGdvdCAweCR7YmF0Y2hbMF0/LnRvU3RyaW5nKDE2KX1gXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuY29tcHJlc3Npb24gPT09IG51bGwpIHtcbiAgICAgICAgICAgIHJldHVybiB7IGFsZ29yaXRobTogUGFja2V0Q29tcHJlc3Npb25BbGdvcml0aG0uTk9ORSwgYm9keTogYmF0Y2guc3ViYXJyYXkoMSkgfTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChiYXRjaC5ieXRlTGVuZ3RoIDwgMikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdUcnVuY2F0ZWQgYmF0Y2g6IGNvbXByZXNzaW9uIGlzIG5lZ290aWF0ZWQgYnV0IHRoZSBhbGdvcml0aG0gYnl0ZSBpcyBtaXNzaW5nJyk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4geyBhbGdvcml0aG06IENvbXByZXNzaW9uQ29kZWMuZnJvbVByZWZpeChiYXRjaFsxXSEpLCBib2R5OiBiYXRjaC5zdWJhcnJheSgyKSB9O1xuICAgIH1cblxuICAgIHByaXZhdGUgc3RhdGljIGpvaW5QYWNrZXRzKHBhY2tldHM6IHJlYWRvbmx5IEJ1ZmZlcltdKTogQnVmZmVyIHtcbiAgICAgICAgY29uc3Qgc3RyZWFtID0gbmV3IEJpbmFyeVN0cmVhbSgpO1xuICAgICAgICBmb3IgKGNvbnN0IHBhY2tldCBvZiBwYWNrZXRzKSB7XG4gICAgICAgICAgICBzdHJlYW0ud3JpdGVVbnNpZ25lZFZhckludChwYWNrZXQuYnl0ZUxlbmd0aCk7XG4gICAgICAgICAgICBzdHJlYW0ud3JpdGUocGFja2V0KTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBzdHJlYW0uZ2V0QnVmZmVyKCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU3BsaXRzIHRoZSBkZWNvbXByZXNzZWQgcGF5bG9hZCBiYWNrIGludG8gcGFja2V0cy5cbiAgICAgKlxuICAgICAqIEEgbWFudWFsIGN1cnNvciByYXRoZXIgdGhhbiBhIGBCaW5hcnlTdHJlYW1gLCBmb3IgdGhlIG9mZnNldHM6IGV2ZXJ5IGxlbmd0aCBoZXJlIGlzXG4gICAgICogcGVlci1jb250cm9sbGVkIGFuZCBoYXMgdG8gYmUgY2hlY2tlZCBhZ2FpbnN0IHdoYXQgaXMgYWN0dWFsbHkgbGVmdCwgYW5kIHNheWluZ1xuICAgICAqICp3aGVyZSogYSBiYXRjaCB3ZW50IHdyb25nIGlzIG1vc3Qgb2YgdGhlIHZhbHVlIG9mIG5vdGljaW5nLiBBbiBlbXB0eSBwYXlsb2FkIHlpZWxkc1xuICAgICAqIG5vIHBhY2tldHMgcmF0aGVyIHRoYW4gdGhyb3dpbmcgLSB0aGUgYGRvL3doaWxlYCB0aGlzIHJlcGxhY2VkIGFsd2F5cyByYW4gb25jZSBhbmRcbiAgICAgKiB0cmllZCB0byByZWFkIGEgdmFyaW50IG91dCBvZiBub3RoaW5nLlxuICAgICAqL1xuICAgIHByaXZhdGUgc3RhdGljIHNwbGl0UGFja2V0cyhwYXlsb2FkOiBCdWZmZXIpOiBCdWZmZXJbXSB7XG4gICAgICAgIGNvbnN0IHBhY2tldHM6IEJ1ZmZlcltdID0gW107XG4gICAgICAgIGxldCBjdXJzb3IgPSAwO1xuXG4gICAgICAgIHdoaWxlIChjdXJzb3IgPCBwYXlsb2FkLmJ5dGVMZW5ndGgpIHtcbiAgICAgICAgICAgIGNvbnN0IHN0YXJ0ID0gY3Vyc29yO1xuICAgICAgICAgICAgY29uc3QgeyB2YWx1ZTogbGVuZ3RoLCBuZXh0IH0gPSBCYXRjaENvZGVjLnJlYWRVbnNpZ25lZFZhckludChwYXlsb2FkLCBjdXJzb3IpO1xuICAgICAgICAgICAgY3Vyc29yID0gbmV4dDtcblxuICAgICAgICAgICAgaWYgKGxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgTWFsZm9ybWVkIGJhdGNoOiB6ZXJvIGxlbmd0aCBwYWNrZXQgYXQgb2Zmc2V0ICR7c3RhcnR9YCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAobGVuZ3RoID4gcGF5bG9hZC5ieXRlTGVuZ3RoIC0gY3Vyc29yKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgICAgICBgTWFsZm9ybWVkIGJhdGNoOiBwYWNrZXQgYXQgb2Zmc2V0ICR7c3RhcnR9IGRlY2xhcmVzICR7bGVuZ3RofSBieXRlcywgJHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHBheWxvYWQuYnl0ZUxlbmd0aCAtIGN1cnNvclxuICAgICAgICAgICAgICAgICAgICB9IHJlbWFpbmBcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBwYWNrZXRzLnB1c2gocGF5bG9hZC5zdWJhcnJheShjdXJzb3IsIGN1cnNvciArIGxlbmd0aCkpO1xuICAgICAgICAgICAgY3Vyc29yICs9IGxlbmd0aDtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBwYWNrZXRzO1xuICAgIH1cblxuICAgIC8qKiBMRUIxMjgsIG1hdGNoaW5nIGBCaW5hcnlTdHJlYW0ucmVhZFVuc2lnbmVkVmFySW50YCBpbmNsdWRpbmcgaXRzIG92ZXItbG9uZyByZWplY3Rpb24uICovXG4gICAgcHJpdmF0ZSBzdGF0aWMgcmVhZFVuc2lnbmVkVmFySW50KGJ1ZmZlcjogQnVmZmVyLCBvZmZzZXQ6IG51bWJlcik6IHsgdmFsdWU6IG51bWJlcjsgbmV4dDogbnVtYmVyIH0ge1xuICAgICAgICBsZXQgdmFsdWUgPSAwO1xuICAgICAgICBsZXQgY3Vyc29yID0gb2Zmc2V0O1xuXG4gICAgICAgIGZvciAobGV0IHNoaWZ0ID0gMDsgc2hpZnQgPD0gMjg7IHNoaWZ0ICs9IDcpIHtcbiAgICAgICAgICAgIGlmIChjdXJzb3IgPj0gYnVmZmVyLmJ5dGVMZW5ndGgpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE1hbGZvcm1lZCBiYXRjaDogdmFyaW50IGF0IG9mZnNldCAke29mZnNldH0gcnVucyBwYXN0IHRoZSBlbmQgb2YgdGhlIHBheWxvYWRgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgYnl0ZSA9IGJ1ZmZlcltjdXJzb3IrK10hO1xuICAgICAgICAgICAgdmFsdWUgfD0gKGJ5dGUgJiAweDdmKSA8PCBzaGlmdDtcblxuICAgICAgICAgICAgaWYgKChieXRlICYgMHg4MCkgPT09IDApIHtcbiAgICAgICAgICAgICAgICAvLyBCaXRzIGFib3ZlIHRoZSAzMm5kIHdvdWxkIG90aGVyd2lzZSBiZSBzaGlmdGVkIG9mZiBhbmQgZGlzY2FyZGVkLCBzbyBhblxuICAgICAgICAgICAgICAgIC8vIG92ZXItbG9uZyBlbmNvZGluZyBzdWNoIGFzIGBmZiBmZiBmZiBmZiA3ZmAgd291bGQgZGVjb2RlIHRvIHRoZSBzYW1lXG4gICAgICAgICAgICAgICAgLy8gdmFsdWUgYXMgdGhlIGNhbm9uaWNhbCBgZmYgZmYgZmYgZmYgMGZgLlxuICAgICAgICAgICAgICAgIGlmIChzaGlmdCA9PT0gMjggJiYgKGJ5dGUgJiAweDcwKSAhPT0gMCkge1xuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE1hbGZvcm1lZCBiYXRjaDogdmFyaW50IGF0IG9mZnNldCAke29mZnNldH0gb3ZlcmZsb3dzIDMyIGJpdHNgKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBgPj4+IDBgIGJlY2F1c2UgdGhlIGFjY3VtdWxhdG9yIGlzIGludDMyOiB3aXRob3V0IGl0IGV2ZXJ5dGhpbmcgZnJvbSAyXjMxXG4gICAgICAgICAgICAgICAgLy8gdXAgY29tZXMgYmFjayBuZWdhdGl2ZS5cbiAgICAgICAgICAgICAgICByZXR1cm4geyB2YWx1ZTogdmFsdWUgPj4+IDAsIG5leHQ6IGN1cnNvciB9O1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBNYWxmb3JtZWQgYmF0Y2g6IHZhcmludCBhdCBvZmZzZXQgJHtvZmZzZXR9IGRp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
@@ -0,0 +1,59 @@
1
+ import type { CompressionSettings } from './CompressionCodec';
2
+ import { CompressionCodec } from './CompressionCodec';
3
+ /** The id every batch carries, and the only packet id RakNet ever sees from Minecraft. */
4
+ export declare const BATCH_PACKET_ID = 254;
5
+ /**
6
+ * A batch: the outermost Minecraft layer, sitting directly inside a RakNet frame.
7
+ *
8
+ * ```
9
+ * 0xFE │ [algorithm byte] │ ⟨varint len, packet⟩ ⟨varint len, packet⟩ …
10
+ * ```
11
+ *
12
+ * The algorithm byte is present if and only if compression has been *negotiated* - which is
13
+ * not the same as this batch being compressed, and conflating the two is the trap here. A
14
+ * connection sends `RequestNetworkSettings` and receives `NetworkSettings` with no prefix at
15
+ * all; from the packet after that one, every batch carries a prefix, and a peer is free to
16
+ * make it {@link UNCOMPRESSED_BATCH} whenever compressing would not pay. So the two states
17
+ * are two codecs - {@link BatchCodec.uncompressed} and {@link BatchCodec.compressed} - and a
18
+ * connection replaces one with the other exactly once.
19
+ *
20
+ * Nothing here knows what a packet *is*: a batch is a list of opaque buffers. That is what
21
+ * lets a proxy route on ids it has no definition for, and what keeps this usable from both
22
+ * sides of a connection.
23
+ */
24
+ export declare class BatchCodec {
25
+ private readonly compression;
26
+ private constructor();
27
+ /**
28
+ * The pre-negotiation form, carrying no algorithm byte. Only `RequestNetworkSettings`
29
+ * and `NetworkSettings` itself travel this way.
30
+ */
31
+ static uncompressed(): BatchCodec;
32
+ /** The post-negotiation form: every batch carries an algorithm byte. */
33
+ static compressed(settings: CompressionSettings | CompressionCodec): BatchCodec;
34
+ /** Whether compression has been negotiated, i.e. whether batches carry a prefix byte. */
35
+ isCompressionEnabled(): boolean;
36
+ encode(packets: readonly Buffer[]): Buffer;
37
+ /** The same, with the deflate handed to zlib's asynchronous API. */
38
+ encodeAsync(packets: readonly Buffer[]): Promise<Buffer>;
39
+ decode(batch: Buffer): Buffer[];
40
+ /** The same, with the inflate handed to zlib's asynchronous API. */
41
+ decodeAsync(batch: Buffer): Promise<Buffer[]>;
42
+ private static frame;
43
+ /** Strips the id and, when compression is on, the algorithm byte. */
44
+ private split;
45
+ private static joinPackets;
46
+ /**
47
+ * Splits the decompressed payload back into packets.
48
+ *
49
+ * A manual cursor rather than a `BinaryStream`, for the offsets: every length here is
50
+ * peer-controlled and has to be checked against what is actually left, and saying
51
+ * *where* a batch went wrong is most of the value of noticing. An empty payload yields
52
+ * no packets rather than throwing - the `do/while` this replaced always ran once and
53
+ * tried to read a varint out of nothing.
54
+ */
55
+ private static splitPackets;
56
+ /** LEB128, matching `BinaryStream.readUnsignedVarInt` including its over-long rejection. */
57
+ private static readUnsignedVarInt;
58
+ }
59
+ //# sourceMappingURL=BatchCodec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BatchCodec.d.ts","sourceRoot":"","sources":["../src/BatchCodec.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,0FAA0F;AAC1F,eAAO,MAAM,eAAe,MAAO,CAAC;AAEpC;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,UAAU;IACC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAAhD,OAAO,eAAsE;IAE7E;;;OAGG;IACH,OAAc,YAAY,IAAI,UAAU,CAEvC;IAED,wEAAwE;IACxE,OAAc,UAAU,CAAC,QAAQ,EAAE,mBAAmB,GAAG,gBAAgB,GAAG,UAAU,CAErF;IAED,yFAAyF;IAClF,oBAAoB,IAAI,OAAO,CAErC;IAEM,MAAM,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAShD;IAED,oEAAoE;IACvD,WAAW,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CASpE;IAEM,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAIrC;IAED,oEAAoE;IACvD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAMzD;IAED,OAAO,CAAC,MAAM,CAAC,KAAK;IAIpB,qEAAqE;IACrE,OAAO,CAAC,KAAK;IAkBb,OAAO,CAAC,MAAM,CAAC,WAAW;IAU1B;;;;;;;;OAQG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IA2B3B,4FAA4F;IAC5F,OAAO,CAAC,MAAM,CAAC,kBAAkB;CA4BpC"}
@@ -0,0 +1,136 @@
1
+ import { CompressionCodec } from "./CompressionCodec.es.js";
2
+ import BinaryStream from "@jsprismarine/binaryutils";
3
+ import { PacketCompressionAlgorithm } from "@jsprismarine/minecraft";
4
+ // src/BatchCodec.ts
5
+ /** The id every batch carries, and the only packet id RakNet ever sees from Minecraft. */
6
+ var BATCH_PACKET_ID = 254;
7
+ /**
8
+ * A batch: the outermost Minecraft layer, sitting directly inside a RakNet frame.
9
+ *
10
+ * ```
11
+ * 0xFE │ [algorithm byte] │ ⟨varint len, packet⟩ ⟨varint len, packet⟩ …
12
+ * ```
13
+ *
14
+ * The algorithm byte is present if and only if compression has been *negotiated* - which is
15
+ * not the same as this batch being compressed, and conflating the two is the trap here. A
16
+ * connection sends `RequestNetworkSettings` and receives `NetworkSettings` with no prefix at
17
+ * all; from the packet after that one, every batch carries a prefix, and a peer is free to
18
+ * make it {@link UNCOMPRESSED_BATCH} whenever compressing would not pay. So the two states
19
+ * are two codecs - {@link BatchCodec.uncompressed} and {@link BatchCodec.compressed} - and a
20
+ * connection replaces one with the other exactly once.
21
+ *
22
+ * Nothing here knows what a packet *is*: a batch is a list of opaque buffers. That is what
23
+ * lets a proxy route on ids it has no definition for, and what keeps this usable from both
24
+ * sides of a connection.
25
+ */
26
+ var BatchCodec = class BatchCodec {
27
+ compression;
28
+ constructor(compression) {
29
+ this.compression = compression;
30
+ }
31
+ /**
32
+ * The pre-negotiation form, carrying no algorithm byte. Only `RequestNetworkSettings`
33
+ * and `NetworkSettings` itself travel this way.
34
+ */
35
+ static uncompressed() {
36
+ return new BatchCodec(null);
37
+ }
38
+ /** The post-negotiation form: every batch carries an algorithm byte. */
39
+ static compressed(settings) {
40
+ return new BatchCodec(settings instanceof CompressionCodec ? settings : new CompressionCodec(settings));
41
+ }
42
+ /** Whether compression has been negotiated, i.e. whether batches carry a prefix byte. */
43
+ isCompressionEnabled() {
44
+ return this.compression !== null;
45
+ }
46
+ encode(packets) {
47
+ const payload = BatchCodec.joinPackets(packets);
48
+ if (this.compression === null) return Buffer.concat([Buffer.from([254]), payload]);
49
+ const algorithm = this.compression.algorithmFor(payload.byteLength);
50
+ return BatchCodec.frame(algorithm, this.compression.compress(payload));
51
+ }
52
+ /** The same, with the deflate handed to zlib's asynchronous API. */
53
+ async encodeAsync(packets) {
54
+ const payload = BatchCodec.joinPackets(packets);
55
+ if (this.compression === null) return Buffer.concat([Buffer.from([254]), payload]);
56
+ const algorithm = this.compression.algorithmFor(payload.byteLength);
57
+ return BatchCodec.frame(algorithm, await this.compression.compressAsync(payload));
58
+ }
59
+ decode(batch) {
60
+ const { algorithm, body } = this.split(batch);
61
+ return BatchCodec.splitPackets(this.compression === null ? body : this.compression.decompress(algorithm, body));
62
+ }
63
+ /** The same, with the inflate handed to zlib's asynchronous API. */
64
+ async decodeAsync(batch) {
65
+ const { algorithm, body } = this.split(batch);
66
+ return BatchCodec.splitPackets(this.compression === null ? body : await this.compression.decompressAsync(algorithm, body));
67
+ }
68
+ static frame(algorithm, body) {
69
+ return Buffer.concat([Buffer.from([254, CompressionCodec.toPrefix(algorithm)]), body]);
70
+ }
71
+ /** Strips the id and, when compression is on, the algorithm byte. */
72
+ split(batch) {
73
+ if (batch.byteLength < 1 || batch[0] !== 254) throw new Error(`Not a batch: expected id 0x${254 .toString(16)}, got 0x${batch[0]?.toString(16)}`);
74
+ if (this.compression === null) return {
75
+ algorithm: PacketCompressionAlgorithm.NONE,
76
+ body: batch.subarray(1)
77
+ };
78
+ if (batch.byteLength < 2) throw new Error("Truncated batch: compression is negotiated but the algorithm byte is missing");
79
+ return {
80
+ algorithm: CompressionCodec.fromPrefix(batch[1]),
81
+ body: batch.subarray(2)
82
+ };
83
+ }
84
+ static joinPackets(packets) {
85
+ const stream = new BinaryStream();
86
+ for (const packet of packets) {
87
+ stream.writeUnsignedVarInt(packet.byteLength);
88
+ stream.write(packet);
89
+ }
90
+ return stream.getBuffer();
91
+ }
92
+ /**
93
+ * Splits the decompressed payload back into packets.
94
+ *
95
+ * A manual cursor rather than a `BinaryStream`, for the offsets: every length here is
96
+ * peer-controlled and has to be checked against what is actually left, and saying
97
+ * *where* a batch went wrong is most of the value of noticing. An empty payload yields
98
+ * no packets rather than throwing - the `do/while` this replaced always ran once and
99
+ * tried to read a varint out of nothing.
100
+ */
101
+ static splitPackets(payload) {
102
+ const packets = [];
103
+ let cursor = 0;
104
+ while (cursor < payload.byteLength) {
105
+ const start = cursor;
106
+ const { value: length, next } = BatchCodec.readUnsignedVarInt(payload, cursor);
107
+ cursor = next;
108
+ if (length === 0) throw new Error(`Malformed batch: zero length packet at offset ${start}`);
109
+ if (length > payload.byteLength - cursor) throw new Error(`Malformed batch: packet at offset ${start} declares ${length} bytes, ${payload.byteLength - cursor} remain`);
110
+ packets.push(payload.subarray(cursor, cursor + length));
111
+ cursor += length;
112
+ }
113
+ return packets;
114
+ }
115
+ /** LEB128, matching `BinaryStream.readUnsignedVarInt` including its over-long rejection. */
116
+ static readUnsignedVarInt(buffer, offset) {
117
+ let value = 0;
118
+ let cursor = offset;
119
+ for (let shift = 0; shift <= 28; shift += 7) {
120
+ if (cursor >= buffer.byteLength) throw new Error(`Malformed batch: varint at offset ${offset} runs past the end of the payload`);
121
+ const byte = buffer[cursor++];
122
+ value |= (byte & 127) << shift;
123
+ if ((byte & 128) === 0) {
124
+ if (shift === 28 && (byte & 112) !== 0) throw new Error(`Malformed batch: varint at offset ${offset} overflows 32 bits`);
125
+ return {
126
+ value: value >>> 0,
127
+ next: cursor
128
+ };
129
+ }
130
+ }
131
+ throw new Error(`Malformed batch: varint at offset ${offset} did not terminate after 5 bytes`);
132
+ }
133
+ };
134
+ export { BATCH_PACKET_ID, BatchCodec };
135
+
136
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQmF0Y2hDb2RlYy5lcy5qcyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi9zcmMvQmF0Y2hDb2RlYy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgQmluYXJ5U3RyZWFtIGZyb20gJ0Bqc3ByaXNtYXJpbmUvYmluYXJ5dXRpbHMnO1xuaW1wb3J0IHsgUGFja2V0Q29tcHJlc3Npb25BbGdvcml0aG0gfSBmcm9tICdAanNwcmlzbWFyaW5lL21pbmVjcmFmdCc7XG5pbXBvcnQgdHlwZSB7IENvbXByZXNzaW9uU2V0dGluZ3MgfSBmcm9tICcuL0NvbXByZXNzaW9uQ29kZWMnO1xuaW1wb3J0IHsgQ29tcHJlc3Npb25Db2RlYyB9IGZyb20gJy4vQ29tcHJlc3Npb25Db2RlYyc7XG5cbi8qKiBUaGUgaWQgZXZlcnkgYmF0Y2ggY2FycmllcywgYW5kIHRoZSBvbmx5IHBhY2tldCBpZCBSYWtOZXQgZXZlciBzZWVzIGZyb20gTWluZWNyYWZ0LiAqL1xuZXhwb3J0IGNvbnN0IEJBVENIX1BBQ0tFVF9JRCA9IDB4ZmU7XG5cbi8qKlxuICogQSBiYXRjaDogdGhlIG91dGVybW9zdCBNaW5lY3JhZnQgbGF5ZXIsIHNpdHRpbmcgZGlyZWN0bHkgaW5zaWRlIGEgUmFrTmV0IGZyYW1lLlxuICpcbiAqIGBgYFxuICogMHhGRSDilIIgW2FsZ29yaXRobSBieXRlXSDilIIg4p+odmFyaW50IGxlbiwgcGFja2V04p+pIOKfqHZhcmludCBsZW4sIHBhY2tldOKfqSDigKZcbiAqIGBgYFxuICpcbiAqIFRoZSBhbGdvcml0aG0gYnl0ZSBpcyBwcmVzZW50IGlmIGFuZCBvbmx5IGlmIGNvbXByZXNzaW9uIGhhcyBiZWVuICpuZWdvdGlhdGVkKiAtIHdoaWNoIGlzXG4gKiBub3QgdGhlIHNhbWUgYXMgdGhpcyBiYXRjaCBiZWluZyBjb21wcmVzc2VkLCBhbmQgY29uZmxhdGluZyB0aGUgdHdvIGlzIHRoZSB0cmFwIGhlcmUuIEFcbiAqIGNvbm5lY3Rpb24gc2VuZHMgYFJlcXVlc3ROZXR3b3JrU2V0dGluZ3NgIGFuZCByZWNlaXZlcyBgTmV0d29ya1NldHRpbmdzYCB3aXRoIG5vIHByZWZpeCBhdFxuICogYWxsOyBmcm9tIHRoZSBwYWNrZXQgYWZ0ZXIgdGhhdCBvbmUsIGV2ZXJ5IGJhdGNoIGNhcnJpZXMgYSBwcmVmaXgsIGFuZCBhIHBlZXIgaXMgZnJlZSB0b1xuICogbWFrZSBpdCB7QGxpbmsgVU5DT01QUkVTU0VEX0JBVENIfSB3aGVuZXZlciBjb21wcmVzc2luZyB3b3VsZCBub3QgcGF5LiBTbyB0aGUgdHdvIHN0YXRlc1xuICogYXJlIHR3byBjb2RlY3MgLSB7QGxpbmsgQmF0Y2hDb2RlYy51bmNvbXByZXNzZWR9IGFuZCB7QGxpbmsgQmF0Y2hDb2RlYy5jb21wcmVzc2VkfSAtIGFuZCBhXG4gKiBjb25uZWN0aW9uIHJlcGxhY2VzIG9uZSB3aXRoIHRoZSBvdGhlciBleGFjdGx5IG9uY2UuXG4gKlxuICogTm90aGluZyBoZXJlIGtub3dzIHdoYXQgYSBwYWNrZXQgKmlzKjogYSBiYXRjaCBpcyBhIGxpc3Qgb2Ygb3BhcXVlIGJ1ZmZlcnMuIFRoYXQgaXMgd2hhdFxuICogbGV0cyBhIHByb3h5IHJvdXRlIG9uIGlkcyBpdCBoYXMgbm8gZGVmaW5pdGlvbiBmb3IsIGFuZCB3aGF0IGtlZXBzIHRoaXMgdXNhYmxlIGZyb20gYm90aFxuICogc2lkZXMgb2YgYSBjb25uZWN0aW9uLlxuICovXG5leHBvcnQgY2xhc3MgQmF0Y2hDb2RlYyB7XG4gICAgcHJpdmF0ZSBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IGNvbXByZXNzaW9uOiBDb21wcmVzc2lvbkNvZGVjIHwgbnVsbCkge31cblxuICAgIC8qKlxuICAgICAqIFRoZSBwcmUtbmVnb3RpYXRpb24gZm9ybSwgY2Fycnlpbmcgbm8gYWxnb3JpdGhtIGJ5dGUuIE9ubHkgYFJlcXVlc3ROZXR3b3JrU2V0dGluZ3NgXG4gICAgICogYW5kIGBOZXR3b3JrU2V0dGluZ3NgIGl0c2VsZiB0cmF2ZWwgdGhpcyB3YXkuXG4gICAgICovXG4gICAgcHVibGljIHN0YXRpYyB1bmNvbXByZXNzZWQoKTogQmF0Y2hDb2RlYyB7XG4gICAgICAgIHJldHVybiBuZXcgQmF0Y2hDb2RlYyhudWxsKTtcbiAgICB9XG5cbiAgICAvKiogVGhlIHBvc3QtbmVnb3RpYXRpb24gZm9ybTogZXZlcnkgYmF0Y2ggY2FycmllcyBhbiBhbGdvcml0aG0gYnl0ZS4gKi9cbiAgICBwdWJsaWMgc3RhdGljIGNvbXByZXNzZWQoc2V0dGluZ3M6IENvbXByZXNzaW9uU2V0dGluZ3MgfCBDb21wcmVzc2lvbkNvZGVjKTogQmF0Y2hDb2RlYyB7XG4gICAgICAgIHJldHVybiBuZXcgQmF0Y2hDb2RlYyhzZXR0aW5ncyBpbnN0YW5jZW9mIENvbXByZXNzaW9uQ29kZWMgPyBzZXR0aW5ncyA6IG5ldyBDb21wcmVzc2lvbkNvZGVjKHNldHRpbmdzKSk7XG4gICAgfVxuXG4gICAgLyoqIFdoZXRoZXIgY29tcHJlc3Npb24gaGFzIGJlZW4gbmVnb3RpYXRlZCwgaS5lLiB3aGV0aGVyIGJhdGNoZXMgY2FycnkgYSBwcmVmaXggYnl0ZS4gKi9cbiAgICBwdWJsaWMgaXNDb21wcmVzc2lvbkVuYWJsZWQoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbXByZXNzaW9uICE9PSBudWxsO1xuICAgIH1cblxuICAgIHB1YmxpYyBlbmNvZGUocGFja2V0czogcmVhZG9ubHkgQnVmZmVyW10pOiBCdWZmZXIge1xuICAgICAgICBjb25zdCBwYXlsb2FkID0gQmF0Y2hDb2RlYy5qb2luUGFja2V0cyhwYWNrZXRzKTtcblxuICAgICAgICBpZiAodGhpcy5jb21wcmVzc2lvbiA9PT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuIEJ1ZmZlci5jb25jYXQoW0J1ZmZlci5mcm9tKFtCQVRDSF9QQUNLRVRfSURdKSwgcGF5bG9hZF0pO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgYWxnb3JpdGhtID0gdGhpcy5jb21wcmVzc2lvbi5hbGdvcml0aG1Gb3IocGF5bG9hZC5ieXRlTGVuZ3RoKTtcbiAgICAgICAgcmV0dXJuIEJhdGNoQ29kZWMuZnJhbWUoYWxnb3JpdGhtLCB0aGlzLmNvbXByZXNzaW9uLmNvbXByZXNzKHBheWxvYWQpKTtcbiAgICB9XG5cbiAgICAvKiogVGhlIHNhbWUsIHdpdGggdGhlIGRlZmxhdGUgaGFuZGVkIHRvIHpsaWIncyBhc3luY2hyb25vdXMgQVBJLiAqL1xuICAgIHB1YmxpYyBhc3luYyBlbmNvZGVBc3luYyhwYWNrZXRzOiByZWFkb25seSBCdWZmZXJbXSk6IFByb21pc2U8QnVmZmVyPiB7XG4gICAgICAgIGNvbnN0IHBheWxvYWQgPSBCYXRjaENvZGVjLmpvaW5QYWNrZXRzKHBhY2tldHMpO1xuXG4gICAgICAgIGlmICh0aGlzLmNvbXByZXNzaW9uID09PSBudWxsKSB7XG4gICAgICAgICAgICByZXR1cm4gQnVmZmVyLmNvbmNhdChbQnVmZmVyLmZyb20oW0JBVENIX1BBQ0tFVF9JRF0pLCBwYXlsb2FkXSk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBhbGdvcml0aG0gPSB0aGlzLmNvbXByZXNzaW9uLmFsZ29yaXRobUZvcihwYXlsb2FkLmJ5dGVMZW5ndGgpO1xuICAgICAgICByZXR1cm4gQmF0Y2hDb2RlYy5mcmFtZShhbGdvcml0aG0sIGF3YWl0IHRoaXMuY29tcHJlc3Npb24uY29tcHJlc3NBc3luYyhwYXlsb2FkKSk7XG4gICAgfVxuXG4gICAgcHVibGljIGRlY29kZShiYXRjaDogQnVmZmVyKTogQnVmZmVyW10ge1xuICAgICAgICBjb25zdCB7IGFsZ29yaXRobSwgYm9keSB9ID0gdGhpcy5zcGxpdChiYXRjaCk7XG5cbiAgICAgICAgcmV0dXJuIEJhdGNoQ29kZWMuc3BsaXRQYWNrZXRzKHRoaXMuY29tcHJlc3Npb24gPT09IG51bGwgPyBib2R5IDogdGhpcy5jb21wcmVzc2lvbi5kZWNvbXByZXNzKGFsZ29yaXRobSwgYm9keSkpO1xuICAgIH1cblxuICAgIC8qKiBUaGUgc2FtZSwgd2l0aCB0aGUgaW5mbGF0ZSBoYW5kZWQgdG8gemxpYidzIGFzeW5jaHJvbm91cyBBUEkuICovXG4gICAgcHVibGljIGFzeW5jIGRlY29kZUFzeW5jKGJhdGNoOiBCdWZmZXIpOiBQcm9taXNlPEJ1ZmZlcltdPiB7XG4gICAgICAgIGNvbnN0IHsgYWxnb3JpdGhtLCBib2R5IH0gPSB0aGlzLnNwbGl0KGJhdGNoKTtcblxuICAgICAgICByZXR1cm4gQmF0Y2hDb2RlYy5zcGxpdFBhY2tldHMoXG4gICAgICAgICAgICB0aGlzLmNvbXByZXNzaW9uID09PSBudWxsID8gYm9keSA6IGF3YWl0IHRoaXMuY29tcHJlc3Npb24uZGVjb21wcmVzc0FzeW5jKGFsZ29yaXRobSwgYm9keSlcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHN0YXRpYyBmcmFtZShhbGdvcml0aG06IFBhY2tldENvbXByZXNzaW9uQWxnb3JpdGhtLCBib2R5OiBCdWZmZXIpOiBCdWZmZXIge1xuICAgICAgICByZXR1cm4gQnVmZmVyLmNvbmNhdChbQnVmZmVyLmZyb20oW0JBVENIX1BBQ0tFVF9JRCwgQ29tcHJlc3Npb25Db2RlYy50b1ByZWZpeChhbGdvcml0aG0pXSksIGJvZHldKTtcbiAgICB9XG5cbiAgICAvKiogU3RyaXBzIHRoZSBpZCBhbmQsIHdoZW4gY29tcHJlc3Npb24gaXMgb24sIHRoZSBhbGdvcml0aG0gYnl0ZS4gKi9cbiAgICBwcml2YXRlIHNwbGl0KGJhdGNoOiBCdWZmZXIpOiB7IGFsZ29yaXRobTogUGFja2V0Q29tcHJlc3Npb25BbGdvcml0aG07IGJvZHk6IEJ1ZmZlciB9IHtcbiAgICAgICAgaWYgKGJhdGNoLmJ5dGVMZW5ndGggPCAxIHx8IGJhdGNoWzBdICE9PSBCQVRDSF9QQUNLRVRfSUQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICBgTm90IGEgYmF0Y2g6IGV4cGVjdGVkIGlkIDB4JHtCQVRDSF9QQUNLRVRfSUQudG9TdHJpbmcoMTYpfSwgZ290IDB4JHtiYXRjaFswXT8udG9TdHJpbmcoMTYpfWBcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5jb21wcmVzc2lvbiA9PT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuIHsgYWxnb3JpdGhtOiBQYWNrZXRDb21wcmVzc2lvbkFsZ29yaXRobS5OT05FLCBib2R5OiBiYXRjaC5zdWJhcnJheSgxKSB9O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGJhdGNoLmJ5dGVMZW5ndGggPCAyKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1RydW5jYXRlZCBiYXRjaDogY29tcHJlc3Npb24gaXMgbmVnb3RpYXRlZCBidXQgdGhlIGFsZ29yaXRobSBieXRlIGlzIG1pc3NpbmcnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB7IGFsZ29yaXRobTogQ29tcHJlc3Npb25Db2RlYy5mcm9tUHJlZml4KGJhdGNoWzFdISksIGJvZHk6IGJhdGNoLnN1YmFycmF5KDIpIH07XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzdGF0aWMgam9pblBhY2tldHMocGFja2V0czogcmVhZG9ubHkgQnVmZmVyW10pOiBCdWZmZXIge1xuICAgICAgICBjb25zdCBzdHJlYW0gPSBuZXcgQmluYXJ5U3RyZWFtKCk7XG4gICAgICAgIGZvciAoY29uc3QgcGFja2V0IG9mIHBhY2tldHMpIHtcbiAgICAgICAgICAgIHN0cmVhbS53cml0ZVVuc2lnbmVkVmFySW50KHBhY2tldC5ieXRlTGVuZ3RoKTtcbiAgICAgICAgICAgIHN0cmVhbS53cml0ZShwYWNrZXQpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHN0cmVhbS5nZXRCdWZmZXIoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTcGxpdHMgdGhlIGRlY29tcHJlc3NlZCBwYXlsb2FkIGJhY2sgaW50byBwYWNrZXRzLlxuICAgICAqXG4gICAgICogQSBtYW51YWwgY3Vyc29yIHJhdGhlciB0aGFuIGEgYEJpbmFyeVN0cmVhbWAsIGZvciB0aGUgb2Zmc2V0czogZXZlcnkgbGVuZ3RoIGhlcmUgaXNcbiAgICAgKiBwZWVyLWNvbnRyb2xsZWQgYW5kIGhhcyB0byBiZSBjaGVja2VkIGFnYWluc3Qgd2hhdCBpcyBhY3R1YWxseSBsZWZ0LCBhbmQgc2F5aW5nXG4gICAgICogKndoZXJlKiBhIGJhdGNoIHdlbnQgd3JvbmcgaXMgbW9zdCBvZiB0aGUgdmFsdWUgb2Ygbm90aWNpbmcuIEFuIGVtcHR5IHBheWxvYWQgeWllbGRzXG4gICAgICogbm8gcGFja2V0cyByYXRoZXIgdGhhbiB0aHJvd2luZyAtIHRoZSBgZG8vd2hpbGVgIHRoaXMgcmVwbGFjZWQgYWx3YXlzIHJhbiBvbmNlIGFuZFxuICAgICAqIHRyaWVkIHRvIHJlYWQgYSB2YXJpbnQgb3V0IG9mIG5vdGhpbmcuXG4gICAgICovXG4gICAgcHJpdmF0ZSBzdGF0aWMgc3BsaXRQYWNrZXRzKHBheWxvYWQ6IEJ1ZmZlcik6IEJ1ZmZlcltdIHtcbiAgICAgICAgY29uc3QgcGFja2V0czogQnVmZmVyW10gPSBbXTtcbiAgICAgICAgbGV0IGN1cnNvciA9IDA7XG5cbiAgICAgICAgd2hpbGUgKGN1cnNvciA8IHBheWxvYWQuYnl0ZUxlbmd0aCkge1xuICAgICAgICAgICAgY29uc3Qgc3RhcnQgPSBjdXJzb3I7XG4gICAgICAgICAgICBjb25zdCB7IHZhbHVlOiBsZW5ndGgsIG5leHQgfSA9IEJhdGNoQ29kZWMucmVhZFVuc2lnbmVkVmFySW50KHBheWxvYWQsIGN1cnNvcik7XG4gICAgICAgICAgICBjdXJzb3IgPSBuZXh0O1xuXG4gICAgICAgICAgICBpZiAobGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBNYWxmb3JtZWQgYmF0Y2g6IHplcm8gbGVuZ3RoIHBhY2tldCBhdCBvZmZzZXQgJHtzdGFydH1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChsZW5ndGggPiBwYXlsb2FkLmJ5dGVMZW5ndGggLSBjdXJzb3IpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAgICAgICAgIGBNYWxmb3JtZWQgYmF0Y2g6IHBhY2tldCBhdCBvZmZzZXQgJHtzdGFydH0gZGVjbGFyZXMgJHtsZW5ndGh9IGJ5dGVzLCAke1xuICAgICAgICAgICAgICAgICAgICAgICAgcGF5bG9hZC5ieXRlTGVuZ3RoIC0gY3Vyc29yXG4gICAgICAgICAgICAgICAgICAgIH0gcmVtYWluYFxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHBhY2tldHMucHVzaChwYXlsb2FkLnN1YmFycmF5KGN1cnNvciwgY3Vyc29yICsgbGVuZ3RoKSk7XG4gICAgICAgICAgICBjdXJzb3IgKz0gbGVuZ3RoO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHBhY2tldHM7XG4gICAgfVxuXG4gICAgLyoqIExFQjEyOCwgbWF0Y2hpbmcgYEJpbmFyeVN0cmVhbS5yZWFkVW5zaWduZWRWYXJJbnRgIGluY2x1ZGluZyBpdHMgb3Zlci1sb25nIHJlamVjdGlvbi4gKi9cbiAgICBwcml2YXRlIHN0YXRpYyByZWFkVW5zaWduZWRWYXJJbnQoYnVmZmVyOiBCdWZmZXIsIG9mZnNldDogbnVtYmVyKTogeyB2YWx1ZTogbnVtYmVyOyBuZXh0OiBudW1iZXIgfSB7XG4gICAgICAgIGxldCB2YWx1ZSA9IDA7XG4gICAgICAgIGxldCBjdXJzb3IgPSBvZmZzZXQ7XG5cbiAgICAgICAgZm9yIChsZXQgc2hpZnQgPSAwOyBzaGlmdCA8PSAyODsgc2hpZnQgKz0gNykge1xuICAgICAgICAgICAgaWYgKGN1cnNvciA+PSBidWZmZXIuYnl0ZUxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgTWFsZm9ybWVkIGJhdGNoOiB2YXJpbnQgYXQgb2Zmc2V0ICR7b2Zmc2V0fSBydW5zIHBhc3QgdGhlIGVuZCBvZiB0aGUgcGF5bG9hZGApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBieXRlID0gYnVmZmVyW2N1cnNvcisrXSE7XG4gICAgICAgICAgICB2YWx1ZSB8PSAoYnl0ZSAmIDB4N2YpIDw8IHNoaWZ0O1xuXG4gICAgICAgICAgICBpZiAoKGJ5dGUgJiAweDgwKSA9PT0gMCkge1xuICAgICAgICAgICAgICAgIC8vIEJpdHMgYWJvdmUgdGhlIDMybmQgd291bGQgb3RoZXJ3aXNlIGJlIHNoaWZ0ZWQgb2ZmIGFuZCBkaXNjYXJkZWQsIHNvIGFuXG4gICAgICAgICAgICAgICAgLy8gb3Zlci1sb25nIGVuY29kaW5nIHN1Y2ggYXMgYGZmIGZmIGZmIGZmIDdmYCB3b3VsZCBkZWNvZGUgdG8gdGhlIHNhbWVcbiAgICAgICAgICAgICAgICAvLyB2YWx1ZSBhcyB0aGUgY2Fub25pY2FsIGBmZiBmZiBmZiBmZiAwZmAuXG4gICAgICAgICAgICAgICAgaWYgKHNoaWZ0ID09PSAyOCAmJiAoYnl0ZSAmIDB4NzApICE9PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgTWFsZm9ybWVkIGJhdGNoOiB2YXJpbnQgYXQgb2Zmc2V0ICR7b2Zmc2V0fSBvdmVyZmxvd3MgMzIgYml0c2ApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIGA+Pj4gMGAgYmVjYXVzZSB0aGUgYWNjdW11bGF0b3IgaXMgaW50MzI6IHdpdGhvdXQgaXQgZXZlcnl0aGluZyBmcm9tIDJeMzFcbiAgICAgICAgICAgICAgICAvLyB1cCBjb21lcyBiYWNrIG5lZ2F0aXZlLlxuICAgICAgICAgICAgICAgIHJldHVybiB7IHZhbHVlOiB2YWx1ZSA+Pj4gMCwgbmV4dDogY3Vyc29yIH07XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE1hbGZvcm1lZCBiYXRjaDogdmFyaW50IGF0IG9mZnNldCAke29mZnNldH0gZGlkIG5vdCB0ZXJtaW5hdGUgYWZ0ZXIgNSBieXRlc2ApO1xuICAgIH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7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