@pylonsync/realtime 0.22.11 → 0.22.12

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.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.22.11",
6
+ "version": "0.22.12",
7
7
  "description": "Pylon realtime shard client pieces with no framework: the wire protocol, codecs, and the entity replication decoder and table.",
8
8
  "type": "module",
9
9
  "main": "./src/index.ts",
@@ -0,0 +1,255 @@
1
+ {
2
+ "frames": [
3
+ {
4
+ "ack": 0,
5
+ "codec": 0,
6
+ "frame": "0100000000000000000100000000000000007b22666c6f617473223a5b302e352c2d312e352c312e323334353637383930313233343536372c31652b3330305d2c22696e7473223a5b302c312c3132372c3132382c3235352c3235362c36353533352c36353533362c343239343936373239352c343239343936373239362c2d312c2d33322c2d33332c2d3132382c2d3132392c2d33323736382c2d33323736392c2d323134373438333634382c2d323134373438333634392c393030373139393235343734303939315d2c226e6573746564223a7b2261223a7b2262223a7b2263223a5b312c5b322c5b335d5d5d7d7d7d2c226e6f7468696e67223a6e756c6c2c22706c6179657273223a5b7b22616c697665223a747275652c226870223a3130302c226964223a227031222c2278223a31322e352c2279223a2d332e32357d2c7b22616c697665223a66616c73652c226870223a302c226964223a227032222c2278223a302e302c2279223a31652d377d5d2c22737472696e6773223a5b22222c2261222c22c3a920c3bc20e4b8ade69687222c226120737472696e672074686174206973206c6f6e676572207468616e207468697274792d6f6e652062797465732c20736f206974206e656564732073747238225d2c227469636b5f72617465223a32307d",
7
+ "kind": 1,
8
+ "payload": {
9
+ "floats": [
10
+ 0.5,
11
+ -1.5,
12
+ 1.2345678901234567,
13
+ 1e+300
14
+ ],
15
+ "ints": [
16
+ 0,
17
+ 1,
18
+ 127,
19
+ 128,
20
+ 255,
21
+ 256,
22
+ 65535,
23
+ 65536,
24
+ 4294967295,
25
+ 4294967296,
26
+ -1,
27
+ -32,
28
+ -33,
29
+ -128,
30
+ -129,
31
+ -32768,
32
+ -32769,
33
+ -2147483648,
34
+ -2147483649,
35
+ 9007199254740991
36
+ ],
37
+ "nested": {
38
+ "a": {
39
+ "b": {
40
+ "c": [
41
+ 1,
42
+ [
43
+ 2,
44
+ [
45
+ 3
46
+ ]
47
+ ]
48
+ ]
49
+ }
50
+ }
51
+ },
52
+ "nothing": null,
53
+ "players": [
54
+ {
55
+ "alive": true,
56
+ "hp": 100,
57
+ "id": "p1",
58
+ "x": 12.5,
59
+ "y": -3.25
60
+ },
61
+ {
62
+ "alive": false,
63
+ "hp": 0,
64
+ "id": "p2",
65
+ "x": 0.0,
66
+ "y": 1e-7
67
+ }
68
+ ],
69
+ "strings": [
70
+ "",
71
+ "a",
72
+ "é ü 中文",
73
+ "a string that is longer than thirty-one bytes, so it needs str8"
74
+ ],
75
+ "tick_rate": 20
76
+ },
77
+ "tick": 1
78
+ },
79
+ {
80
+ "ack": 3,
81
+ "codec": 1,
82
+ "frame": "01010000000000000007000000000000000387a6666c6f61747394cb3fe0000000000000cbbff8000000000000cb3ff3c0ca428c59fbcb7e37e43c8800759ca4696e7473dc001400017fcc80ccffcd0100cdffffce00010000ceffffffffcf0000000100000000ffe0d0dfd080d1ff7fd18000d2ffff7fffd280000000d3ffffffff7fffffffcf001fffffffffffffa66e657374656481a16181a16281a163920192029103a76e6f7468696e67c0a7706c61796572739285a5616c697665c3a2687064a26964a27031a178cb4029000000000000a179cbc00a00000000000085a5616c697665c2a2687000a26964a27032a178cb0000000000000000a179cb3e7ad7f29abcaf48a7737472696e677394a0a161acc3a920c3bc20e4b8ade69687d93f6120737472696e672074686174206973206c6f6e676572207468616e207468697274792d6f6e652062797465732c20736f206974206e656564732073747238a97469636b5f7261746514",
83
+ "kind": 1,
84
+ "payload": {
85
+ "floats": [
86
+ 0.5,
87
+ -1.5,
88
+ 1.2345678901234567,
89
+ 1e+300
90
+ ],
91
+ "ints": [
92
+ 0,
93
+ 1,
94
+ 127,
95
+ 128,
96
+ 255,
97
+ 256,
98
+ 65535,
99
+ 65536,
100
+ 4294967295,
101
+ 4294967296,
102
+ -1,
103
+ -32,
104
+ -33,
105
+ -128,
106
+ -129,
107
+ -32768,
108
+ -32769,
109
+ -2147483648,
110
+ -2147483649,
111
+ 9007199254740991
112
+ ],
113
+ "nested": {
114
+ "a": {
115
+ "b": {
116
+ "c": [
117
+ 1,
118
+ [
119
+ 2,
120
+ [
121
+ 3
122
+ ]
123
+ ]
124
+ ]
125
+ }
126
+ }
127
+ },
128
+ "nothing": null,
129
+ "players": [
130
+ {
131
+ "alive": true,
132
+ "hp": 100,
133
+ "id": "p1",
134
+ "x": 12.5,
135
+ "y": -3.25
136
+ },
137
+ {
138
+ "alive": false,
139
+ "hp": 0,
140
+ "id": "p2",
141
+ "x": 0.0,
142
+ "y": 1e-7
143
+ }
144
+ ],
145
+ "strings": [
146
+ "",
147
+ "a",
148
+ "é ü 中文",
149
+ "a string that is longer than thirty-one bytes, so it needs str8"
150
+ ],
151
+ "tick_rate": 20
152
+ },
153
+ "tick": 7
154
+ },
155
+ {
156
+ "ack": 9007199254740990,
157
+ "codec": 0,
158
+ "frame": "0100001fffffffffffff001ffffffffffffe7b7d",
159
+ "kind": 1,
160
+ "payload": {},
161
+ "tick": 9007199254740991
162
+ },
163
+ {
164
+ "ack": 41,
165
+ "codec": 0,
166
+ "frame": "0200000000000000000800000000000000297b22636c69656e745f736571223a34322c22636f6465223a22726174655f6c696d69746564222c226d657373616765223a22746f6f206d616e7920696e70757473227d",
167
+ "kind": 2,
168
+ "payload": {
169
+ "client_seq": 42,
170
+ "code": "rate_limited",
171
+ "message": "too many inputs"
172
+ },
173
+ "tick": 8
174
+ },
175
+ {
176
+ "ack": 41,
177
+ "codec": 1,
178
+ "frame": "02010000000000000008000000000000002983aa636c69656e745f736571c0a4636f6465a7696e76616c6964a76d657373616765b4696e70757420646964206e6f74206465636f6465",
179
+ "kind": 2,
180
+ "payload": {
181
+ "client_seq": null,
182
+ "code": "invalid",
183
+ "message": "input did not decode"
184
+ },
185
+ "tick": 8
186
+ },
187
+ {
188
+ "ack": 41,
189
+ "codec": 0,
190
+ "frame": "0400000000000000000900000000000000297b227368617264223a227a6f6e652d32222c227469636b6574223a2276312e65794a6c654841694f6a46392e736967227d",
191
+ "kind": 4,
192
+ "payload": {
193
+ "shard": "zone-2",
194
+ "ticket": "v1.eyJleHAiOjF9.sig"
195
+ },
196
+ "tick": 9
197
+ }
198
+ ],
199
+ "headerLength": 18,
200
+ "inputs": [
201
+ {
202
+ "client_seq": 1,
203
+ "input": "join",
204
+ "json": "{\"input\":\"join\",\"client_seq\":1}",
205
+ "msgpack": "82a5696e707574a46a6f696eaa636c69656e745f73657101"
206
+ },
207
+ {
208
+ "client_seq": 2,
209
+ "input": {
210
+ "move_to": {
211
+ "x": 120.5,
212
+ "y": 64.0
213
+ }
214
+ },
215
+ "json": "{\"input\":{\"move_to\":{\"x\":120.5,\"y\":64.0}},\"client_seq\":2}",
216
+ "msgpack": "82a5696e70757481a76d6f76655f746f82a178cb405e200000000000a179cb4050000000000000aa636c69656e745f73657102"
217
+ },
218
+ {
219
+ "client_seq": 300,
220
+ "input": {
221
+ "drive": {
222
+ "air": false,
223
+ "pos": [
224
+ 20,
225
+ 0,
226
+ -20
227
+ ],
228
+ "speed": 20.25
229
+ }
230
+ },
231
+ "json": "{\"input\":{\"drive\":{\"air\":false,\"pos\":[20,0,-20],\"speed\":20.25}},\"client_seq\":300}",
232
+ "msgpack": "82a5696e70757481a5647269766583a3616972c2a3706f73931400eca57370656564cb4034400000000000aa636c69656e745f736571cd012c"
233
+ },
234
+ {
235
+ "client_seq": 70000,
236
+ "input": [
237
+ 1,
238
+ -1,
239
+ 70000,
240
+ "x",
241
+ null,
242
+ true
243
+ ],
244
+ "json": "{\"input\":[1,-1,70000,\"x\",null,true],\"client_seq\":70000}",
245
+ "msgpack": "82a5696e7075749601ffce00011170a178c0c3aa636c69656e745f736571ce00011170"
246
+ },
247
+ {
248
+ "client_seq": 4294967296,
249
+ "input": {},
250
+ "json": "{\"input\":{},\"client_seq\":4294967296}",
251
+ "msgpack": "82a5696e70757480aa636c69656e745f736571cf0000000100000000"
252
+ }
253
+ ],
254
+ "version": 2
255
+ }
package/src/wire.test.ts CHANGED
@@ -87,3 +87,45 @@ test("ack batches stay within the datagram size", () => {
87
87
  const big = encodeDatagramAckBatches(Array.from({ length: 1300 }, (_, i) => [i % 100, 1] as [number, number]), 1_000_000);
88
88
  expect(big.length).toBe(Math.ceil(1300 / MAX_ACKS_PER_MESSAGE));
89
89
  });
90
+
91
+ import wireFixtures from "./wire.fixtures.json";
92
+
93
+ function hexBytes(hex: string): Uint8Array {
94
+ const out = new Uint8Array(hex.length / 2);
95
+ for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
96
+ return out;
97
+ }
98
+
99
+ describe("the Rust encoders' frames and inputs (wire.fixtures.json)", () => {
100
+ test("frames parse and decode to what the server encoded", () => {
101
+ expect(wireFixtures.version).toBe(2);
102
+ expect(wireFixtures.headerLength).toBe(SHARD_HEADER_LEN);
103
+ for (const [i, f] of wireFixtures.frames.entries()) {
104
+ const bytes = hexBytes(f.frame);
105
+ const parsed = parseShardFrame(bytes.buffer as ArrayBuffer);
106
+ expect(parsed.kind, `frame ${i}`).toBe(f.kind);
107
+ expect(parsed.codec).toBe(f.codec);
108
+ expect(parsed.tick).toBe(f.tick);
109
+ expect(parsed.ack).toBe(f.ack);
110
+ expect(decodeShardPayload(parsed.codec, parsed.payload), `frame ${i}`).toEqual(f.payload);
111
+ if (parsed.kind === ShardFrameKind.InputRejected) {
112
+ const r = decodeShardRejection(parsed.codec, parsed.payload);
113
+ const p = f.payload as { client_seq: number | null; code: string; message: string };
114
+ expect(r).toEqual({ clientSeq: p.client_seq, code: p.code, message: p.message });
115
+ }
116
+ }
117
+ });
118
+
119
+ test("inputs encode to the bytes the server decodes", () => {
120
+ for (const c of wireFixtures.inputs) {
121
+ const packed = encodeShardInput(ShardCodec.MessagePack, c.input, c.client_seq) as Uint8Array;
122
+ // JSON.parse cannot tell 64.0 from 64, so a whole-number float packs
123
+ // as an integer here (the server reads either into an f32). Compare
124
+ // the decoded values; the C# client, which keeps number kinds,
125
+ // matches the bytes exactly.
126
+ expect(msgpackDecode(packed)).toEqual(msgpackDecode(hexBytes(c.msgpack)));
127
+ const text = encodeShardInput(ShardCodec.Json, c.input, c.client_seq) as string;
128
+ expect(JSON.parse(text)).toEqual(JSON.parse(c.json));
129
+ }
130
+ });
131
+ });