raknet-typeo 1.0.0

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.
@@ -0,0 +1,394 @@
1
+ import { EventEmitter } from "events";
2
+ import { BinaryStream } from "./utils/binary-stream.js";
3
+ import { createFrame, readFrames, encodeFrameSet, getFrameSize } from "./protocol/frame.js";
4
+ import { encodeAck, encodeNack, decodeAckLike } from "./protocol/ack.js";
5
+ import {
6
+ MessageId,
7
+ PacketReliability,
8
+ PacketPriority,
9
+ isReliable,
10
+ isOrdered,
11
+ isSequenced,
12
+ ACK_FLAG,
13
+ NACK_FLAG,
14
+ DATAGRAM_FLAG,
15
+ MAX_CHANNELS,
16
+ MAX_SPLIT_COUNT,
17
+ } from "./constants.js";
18
+
19
+ const FRAME_OVERHEAD = 60;
20
+ const ACK_TIMEOUT_MS = 150;
21
+ const PING_INTERVAL_MS = 5_000;
22
+ const TIMEOUT_MS = 20_000;
23
+
24
+ export class Connection extends EventEmitter {
25
+ constructor(address, port, guid, sendFn, mtu) {
26
+ super();
27
+ this.address = address;
28
+ this.port = port;
29
+ this.guid = guid;
30
+ this._sendFn = sendFn;
31
+ this._mtu = Math.min(mtu, 1492);
32
+
33
+ this._sendSequenceNumber = 0;
34
+ this._receiveSequenceNumber = 0;
35
+ this._reliableSendIndex = 0;
36
+ this._splitId = 0;
37
+
38
+ this._orderSendIndex = new Array(MAX_CHANNELS).fill(0);
39
+ this._orderReceiveIndex = new Array(MAX_CHANNELS).fill(0);
40
+ this._sequenceSendIndex = new Array(MAX_CHANNELS).fill(0);
41
+ this._sequenceReceiveIndex = new Array(MAX_CHANNELS).fill(0);
42
+
43
+ this._receivedSequences = new Set();
44
+ this._nackQueue = [];
45
+ this._ackQueue = [];
46
+
47
+ this._reliablePackets = new Map();
48
+ this._splitPackets = new Map();
49
+
50
+ this._holdingQueue = Array.from({ length: MAX_CHANNELS }, () => new Map());
51
+ this._sendQueue = [];
52
+
53
+ this._lastPingTime = Date.now();
54
+ this._lastReceivedTime = Date.now();
55
+ this._pingInterval = undefined;
56
+ this._tickInterval = undefined;
57
+
58
+ this.connected = false;
59
+ }
60
+
61
+ start() {
62
+ this._pingInterval = setInterval(() => {
63
+ if (Date.now() - this._lastReceivedTime > TIMEOUT_MS) {
64
+ this.emit("timeout");
65
+ this.close();
66
+ return;
67
+ }
68
+ if (Date.now() - this._lastPingTime > PING_INTERVAL_MS) this._sendConnectedPing();
69
+ }, 1000);
70
+
71
+ this._tickInterval = setInterval(() => this._tick(), 50);
72
+ }
73
+
74
+ _tick() {
75
+ this._flushAckQueue();
76
+ this._flushNackQueue();
77
+ this._flushSendQueue();
78
+ this._retransmitLostPackets();
79
+ }
80
+
81
+ handleDatagram(buf) {
82
+ this._lastReceivedTime = Date.now();
83
+ const flag = buf[0];
84
+
85
+ // ACK_FLAG = 0xC0, NACK_FLAG = 0xA0 — compare exactly, not via bitmask,
86
+ // because 0xC0 has DATAGRAM_FLAG (0x80) set and would fall through otherwise.
87
+ if (flag === ACK_FLAG) {
88
+ for (const seq of decodeAckLike(buf)) this._reliablePackets.delete(seq);
89
+ return;
90
+ }
91
+
92
+ if (flag === NACK_FLAG) {
93
+ for (const seq of decodeAckLike(buf)) {
94
+ const entry = this._reliablePackets.get(seq);
95
+ if (entry) entry.sendTime = 0;
96
+ }
97
+ return;
98
+ }
99
+
100
+ if ((flag & DATAGRAM_FLAG) === 0) return;
101
+
102
+ const seqNum = buf.readUIntLE(1, 3);
103
+
104
+ if (this._receivedSequences.has(seqNum)) {
105
+ this._ackQueue.push(seqNum);
106
+ return;
107
+ }
108
+
109
+ for (let i = this._receiveSequenceNumber; i < seqNum; i++) {
110
+ if (!this._receivedSequences.has(i)) this._nackQueue.push(i);
111
+ }
112
+
113
+ this._receivedSequences.add(seqNum);
114
+ this._ackQueue.push(seqNum);
115
+
116
+ if (seqNum >= this._receiveSequenceNumber) this._receiveSequenceNumber = seqNum + 1;
117
+
118
+ for (const frame of readFrames(buf)) this._handleFrame(frame);
119
+ }
120
+
121
+ _handleFrame(frame) {
122
+ if (frame.split) { this._handleSplitFrame(frame); return; }
123
+ this._processFrame(frame);
124
+ }
125
+
126
+ _handleSplitFrame(frame) {
127
+ const { splitId, splitCount, splitIndex } = frame;
128
+
129
+ if (!this._splitPackets.has(splitId)) {
130
+ if (this._splitPackets.size > MAX_SPLIT_COUNT) this._splitPackets.clear();
131
+ this._splitPackets.set(splitId, {
132
+ splitId, splitCount,
133
+ received: new Map(),
134
+ reliability : frame.reliability,
135
+ orderIndex : frame.orderIndex,
136
+ orderChannel : frame.orderChannel,
137
+ });
138
+ }
139
+
140
+ const split = this._splitPackets.get(splitId);
141
+ split.received.set(splitIndex, frame.body);
142
+
143
+ if (split.received.size === splitCount) {
144
+ this._splitPackets.delete(splitId);
145
+ const parts = [];
146
+ for (let i = 0; i < splitCount; i++) parts.push(split.received.get(i));
147
+ this._processFrame({
148
+ ...frame,
149
+ split : false,
150
+ body : Buffer.concat(parts),
151
+ orderIndex : split.orderIndex,
152
+ orderChannel : split.orderChannel,
153
+ });
154
+ }
155
+ }
156
+
157
+ _processFrame(frame) {
158
+ if (isSequenced(frame.reliability)) {
159
+ const ch = frame.orderChannel;
160
+ if (frame.sequenceIndex < this._sequenceReceiveIndex[ch]) return;
161
+ this._sequenceReceiveIndex[ch] = frame.sequenceIndex + 1;
162
+ }
163
+
164
+ if (isOrdered(frame.reliability) && !isSequenced(frame.reliability)) {
165
+ const ch = frame.orderChannel;
166
+ const expectedIndex = this._orderReceiveIndex[ch];
167
+ if (frame.orderIndex === expectedIndex) {
168
+ this._orderReceiveIndex[ch]++;
169
+ this._handlePacketBody(frame.body);
170
+ this._releaseHeld(ch);
171
+ } else if (frame.orderIndex > expectedIndex) {
172
+ const held = this._holdingQueue[ch];
173
+ if (!held.has(frame.orderIndex)) held.set(frame.orderIndex, new Map([[frame.orderChannel, frame]]));
174
+ }
175
+ return;
176
+ }
177
+
178
+ this._handlePacketBody(frame.body);
179
+ }
180
+
181
+ _releaseHeld(channel) {
182
+ const held = this._holdingQueue[channel];
183
+ let next = this._orderReceiveIndex[channel];
184
+ while (held.has(next)) {
185
+ const frames = held.get(next);
186
+ held.delete(next);
187
+ for (const frame of frames.values()) this._handlePacketBody(frame.body);
188
+ this._orderReceiveIndex[channel]++;
189
+ next++;
190
+ }
191
+ }
192
+
193
+ _handlePacketBody(body) {
194
+ if (body.length === 0) return;
195
+ const id = body[0];
196
+
197
+ switch (id) {
198
+ case MessageId.ConnectedPing: {
199
+ const s = new BinaryStream(body);
200
+ s.skip(1);
201
+ this._sendConnectedPong(s.readInt64BE());
202
+ break;
203
+ }
204
+ case MessageId.ConnectedPong:
205
+ break;
206
+ case MessageId.ConnectionRequest:
207
+ this._handleConnectionRequest(body);
208
+ break;
209
+ case MessageId.ConnectionRequestAccepted:
210
+ this._handleConnectionRequestAccepted(body);
211
+ break;
212
+ case MessageId.NewIncomingConnection:
213
+ if (!this.connected) { this.connected = true; this.emit("connected"); }
214
+ break;
215
+ case MessageId.DisconnectionNotification:
216
+ this.emit("disconnected", "disconnect_notification");
217
+ this.close(false);
218
+ break;
219
+ case MessageId.DetectLostConnections:
220
+ this._sendConnectedPong(BigInt(Date.now()));
221
+ break;
222
+ default:
223
+ this.emit("encapsulated", body);
224
+ break;
225
+ }
226
+ }
227
+
228
+ _handleConnectionRequest(body) {
229
+ const s = new BinaryStream(body);
230
+ s.skip(1);
231
+ const clientGuid = s.readInt64BE();
232
+ const requestTime = s.readInt64BE();
233
+
234
+ const res = new BinaryStream();
235
+ res.writeByte(MessageId.ConnectionRequestAccepted);
236
+ res.writeAddress(this.address, this.port, 4);
237
+ res.writeUInt16BE(0);
238
+ for (let i = 0; i < 20; i++) res.writeAddress("127.0.0.1", 0, 4);
239
+ res.writeInt64BE(requestTime);
240
+ res.writeInt64BE(BigInt(Date.now()));
241
+
242
+ this.sendEncapsulated(res.flush(), PacketPriority.Immediate, PacketReliability.Reliable);
243
+ this.connected = true;
244
+ this.emit("connected", clientGuid);
245
+ }
246
+
247
+ _handleConnectionRequestAccepted(body) {
248
+ const s = new BinaryStream(body);
249
+ s.skip(1);
250
+ s.readAddress();
251
+ s.skip(2);
252
+ for (let i = 0; i < 20; i++) s.readAddress();
253
+ const pingTime = s.readInt64BE();
254
+
255
+ const res = new BinaryStream();
256
+ res.writeByte(MessageId.NewIncomingConnection);
257
+ res.writeAddress("127.0.0.1", 0, 4);
258
+ for (let i = 0; i < 20; i++) res.writeAddress("127.0.0.1", 0, 4);
259
+ res.writeInt64BE(pingTime);
260
+ res.writeInt64BE(BigInt(Date.now()));
261
+
262
+ this.sendEncapsulated(res.flush(), PacketPriority.Immediate, PacketReliability.Reliable);
263
+ this.connected = true;
264
+ this.emit("connected");
265
+ }
266
+
267
+ _sendConnectedPing() {
268
+ this._lastPingTime = Date.now();
269
+ const s = new BinaryStream();
270
+ s.writeByte(MessageId.ConnectedPing);
271
+ s.writeInt64BE(BigInt(Date.now()));
272
+ this.sendEncapsulated(s.flush(), PacketPriority.Immediate, PacketReliability.Unreliable);
273
+ }
274
+
275
+ _sendConnectedPong(pingTime) {
276
+ const s = new BinaryStream();
277
+ s.writeByte(MessageId.ConnectedPong);
278
+ s.writeInt64BE(pingTime);
279
+ s.writeInt64BE(BigInt(Date.now()));
280
+ this.sendEncapsulated(s.flush(), PacketPriority.Immediate, PacketReliability.Unreliable);
281
+ }
282
+
283
+ sendEncapsulated(buf, priority = PacketPriority.Medium, reliability = PacketReliability.ReliableOrdered, orderChannel = 0) {
284
+ const maxBodySize = this._mtu - FRAME_OVERHEAD;
285
+ if (buf.length > maxBodySize) { this._sendSplit(buf, reliability, orderChannel); return; }
286
+
287
+ const frame = createFrame(buf, reliability, orderChannel);
288
+ if (isReliable(reliability)) frame.reliableIndex = this._reliableSendIndex++;
289
+ if (isSequenced(reliability)) frame.sequenceIndex = this._sequenceSendIndex[orderChannel]++;
290
+ if (isOrdered(reliability)) frame.orderIndex = this._orderSendIndex[orderChannel]++;
291
+
292
+ if (priority === PacketPriority.Immediate) this._flushFrames([frame]);
293
+ else this._sendQueue.push(frame);
294
+ }
295
+
296
+ _sendSplit(buf, reliability, orderChannel) {
297
+ const maxBodySize = this._mtu - FRAME_OVERHEAD - 10;
298
+ const splitCount = Math.ceil(buf.length / maxBodySize);
299
+ const id = this._splitId++ & 0xffff;
300
+ const baseOrderIndex = isOrdered(reliability) ? this._orderSendIndex[orderChannel]++ : 0;
301
+
302
+ for (let i = 0; i < splitCount; i++) {
303
+ const chunk = buf.slice(i * maxBodySize, (i + 1) * maxBodySize);
304
+ const frame = createFrame(chunk, reliability, orderChannel);
305
+ frame.split = true;
306
+ frame.splitCount = splitCount;
307
+ frame.splitId = id;
308
+ frame.splitIndex = i;
309
+ if (isReliable(reliability)) frame.reliableIndex = this._reliableSendIndex++;
310
+ if (isOrdered(reliability)) frame.orderIndex = baseOrderIndex;
311
+ this._flushFrames([frame]);
312
+ }
313
+ }
314
+
315
+ _flushSendQueue() {
316
+ if (this._sendQueue.length === 0) return;
317
+ const batch = [];
318
+ let batchSize = 4;
319
+
320
+ while (this._sendQueue.length > 0) {
321
+ const frame = this._sendQueue[0];
322
+ const fSize = getFrameSize(frame);
323
+
324
+ if (batchSize + fSize > this._mtu) {
325
+ if (batch.length > 0) { this._flushFrames(batch.splice(0)); batchSize = 4; }
326
+ else { this._sendQueue.shift(); this._flushFrames([frame]); }
327
+ continue;
328
+ }
329
+ this._sendQueue.shift();
330
+ batch.push(frame);
331
+ batchSize += fSize;
332
+ }
333
+
334
+ if (batch.length > 0) this._flushFrames(batch);
335
+ }
336
+
337
+ _flushFrames(frames) {
338
+ const seqNum = this._sendSequenceNumber++;
339
+ const buf = encodeFrameSet(seqNum, frames);
340
+ this._sendFn(buf, this.address, this.port);
341
+
342
+ for (const frame of frames) {
343
+ if (isReliable(frame.reliability)) {
344
+ this._reliablePackets.set(seqNum, { frame, sendTime: Date.now() });
345
+ break;
346
+ }
347
+ }
348
+ }
349
+
350
+ _retransmitLostPackets() {
351
+ const now = Date.now();
352
+ for (const [seq, entry] of this._reliablePackets) {
353
+ if (entry.sendTime === 0 || now - entry.sendTime > ACK_TIMEOUT_MS * 3) {
354
+ const resendSeq = this._sendSequenceNumber++;
355
+ this._sendFn(encodeFrameSet(resendSeq, [entry.frame]), this.address, this.port);
356
+ this._reliablePackets.delete(seq);
357
+ this._reliablePackets.set(resendSeq, { frame: entry.frame, sendTime: now });
358
+ }
359
+ }
360
+ }
361
+
362
+ _flushAckQueue() {
363
+ if (this._ackQueue.length === 0) return;
364
+ this._sendFn(encodeAck(this._ackQueue.splice(0)), this.address, this.port);
365
+ }
366
+
367
+ _flushNackQueue() {
368
+ if (this._nackQueue.length === 0) return;
369
+ this._sendFn(encodeNack(this._nackQueue.splice(0)), this.address, this.port);
370
+ }
371
+
372
+ sendConnectionRequest(guid) {
373
+ const s = new BinaryStream();
374
+ s.writeByte(MessageId.ConnectionRequest);
375
+ s.writeInt64BE(guid);
376
+ s.writeInt64BE(BigInt(Date.now()));
377
+ s.writeBool(false);
378
+ this.sendEncapsulated(s.flush(), PacketPriority.Immediate, PacketReliability.Reliable);
379
+ }
380
+
381
+ sendDisconnect() {
382
+ const s = new BinaryStream();
383
+ s.writeByte(MessageId.DisconnectionNotification);
384
+ this.sendEncapsulated(s.flush(), PacketPriority.Immediate, PacketReliability.ReliableOrdered);
385
+ }
386
+
387
+ close(notify = true) {
388
+ if (this._pingInterval) clearInterval(this._pingInterval);
389
+ if (this._tickInterval) clearInterval(this._tickInterval);
390
+ if (notify && this.connected) this.sendDisconnect();
391
+ this.connected = false;
392
+ this.removeAllListeners();
393
+ }
394
+ }
@@ -0,0 +1,87 @@
1
+ export const OFFLINE_MESSAGE_DATA_ID = Buffer.from([
2
+ 0x00, 0xff, 0xff, 0x00, 0xfe, 0xfe, 0xfe, 0xfe,
3
+ 0xfd, 0xfd, 0xfd, 0xfd, 0x12, 0x34, 0x56, 0x78,
4
+ ]);
5
+
6
+ export const RAKNET_PROTOCOL_VERSION = 10;
7
+
8
+ export const MessageId = Object.freeze({
9
+ ConnectedPing : 0x00,
10
+ UnconnectedPing : 0x01,
11
+ UnconnectedPingOpenConn : 0x02,
12
+ ConnectedPong : 0x03,
13
+ DetectLostConnections : 0x04,
14
+ OpenConnectionRequest1 : 0x05,
15
+ OpenConnectionReply1 : 0x06,
16
+ OpenConnectionRequest2 : 0x07,
17
+ OpenConnectionReply2 : 0x08,
18
+ ConnectionRequest : 0x09,
19
+ ConnectionRequestAccepted : 0x10,
20
+ ConnectionAttemptFailed : 0x11,
21
+ AlreadyConnected : 0x12,
22
+ NewIncomingConnection : 0x13,
23
+ NoFreeIncomingConnections : 0x14,
24
+ DisconnectionNotification : 0x15,
25
+ ConnectionLost : 0x16,
26
+ ConnectionBanned : 0x17,
27
+ InvalidPassword : 0x18,
28
+ IncompatibleProtocolVersion: 0x19,
29
+ IpRecentlyConnected : 0x1a,
30
+ Timestamp : 0x1b,
31
+ UnconnectedPong : 0x1c,
32
+ AdvertiseSystem : 0x1d,
33
+ DownloadProgress : 0x1e,
34
+ RemoteDisconnected : 0x1f,
35
+ });
36
+
37
+ export const PacketReliability = Object.freeze({
38
+ Unreliable : 0,
39
+ UnreliableSequenced : 1,
40
+ Reliable : 2,
41
+ ReliableOrdered : 3,
42
+ ReliableSequenced : 4,
43
+ UnreliableWithAckReceipt: 5,
44
+ ReliableWithAckReceipt : 6,
45
+ ReliableOrderedWithAckReceipt: 7,
46
+ });
47
+
48
+ export const PacketPriority = Object.freeze({
49
+ Immediate : 0,
50
+ High : 1,
51
+ Medium : 2,
52
+ Low : 3,
53
+ });
54
+
55
+ export function isReliable(reliability) {
56
+ return (
57
+ reliability === PacketReliability.Reliable ||
58
+ reliability === PacketReliability.ReliableOrdered ||
59
+ reliability === PacketReliability.ReliableSequenced ||
60
+ reliability === PacketReliability.ReliableWithAckReceipt ||
61
+ reliability === PacketReliability.ReliableOrderedWithAckReceipt
62
+ );
63
+ }
64
+
65
+ export function isSequenced(reliability) {
66
+ return (
67
+ reliability === PacketReliability.UnreliableSequenced ||
68
+ reliability === PacketReliability.ReliableSequenced
69
+ );
70
+ }
71
+
72
+ export function isOrdered(reliability) {
73
+ return (
74
+ reliability === PacketReliability.ReliableOrdered ||
75
+ reliability === PacketReliability.ReliableOrderedWithAckReceipt ||
76
+ reliability === PacketReliability.UnreliableSequenced ||
77
+ reliability === PacketReliability.ReliableSequenced
78
+ );
79
+ }
80
+
81
+ export const MAX_MTU_SIZE = 1492;
82
+ export const MIN_MTU_SIZE = 400;
83
+ export const MAX_SPLIT_COUNT = 128;
84
+ export const MAX_CHANNELS = 32;
85
+ export const DATAGRAM_FLAG = 0x80;
86
+ export const ACK_FLAG = 0xc0;
87
+ export const NACK_FLAG = 0xa0;
package/src/index.js ADDED
@@ -0,0 +1,52 @@
1
+ export { RakServer } from "./server.js";
2
+ export { RakClient } from "./client.js";
3
+ export { RakWebSocketServer } from "./ws-server.js";
4
+ export { RakWebSocketClient } from "./ws-client.js";
5
+ export { Connection } from "./connection.js";
6
+ export { BinaryStream } from "./utils/binary-stream.js";
7
+ export { UdpTransport } from "./transport/udp.js";
8
+ export { WebSocketServerTransport, WebSocketClientTransport } from "./transport/websocket.js";
9
+
10
+ export {
11
+ MessageId,
12
+ PacketReliability,
13
+ PacketPriority,
14
+ RAKNET_PROTOCOL_VERSION,
15
+ OFFLINE_MESSAGE_DATA_ID,
16
+ MAX_MTU_SIZE,
17
+ MIN_MTU_SIZE,
18
+ MAX_CHANNELS,
19
+ isReliable,
20
+ isSequenced,
21
+ isOrdered,
22
+ } from "./constants.js";
23
+
24
+ export {
25
+ createFrame,
26
+ readFrame,
27
+ readFrames,
28
+ writeFrame,
29
+ encodeFrameSet,
30
+ getFrameSize,
31
+ getFrameHeaderSize,
32
+ } from "./protocol/frame.js";
33
+
34
+ export { encodeAck, encodeNack, decodeAckLike } from "./protocol/ack.js";
35
+
36
+ export {
37
+ isOfflineMessage,
38
+ hasOfflineDataId,
39
+ readUnconnectedPing,
40
+ writeUnconnectedPong,
41
+ writeUnconnectedPing,
42
+ readOpenConnectionRequest1,
43
+ writeOpenConnectionReply1,
44
+ readOpenConnectionRequest2,
45
+ writeOpenConnectionReply2,
46
+ writeIncompatibleProtocolVersion,
47
+ writeNoFreeIncomingConnections,
48
+ writeAlreadyConnected,
49
+ writeConnectionBanned,
50
+ writeOpenConnectionRequest1,
51
+ writeOpenConnectionRequest2,
52
+ } from "./protocol/offline.js";
@@ -0,0 +1,47 @@
1
+ import { BinaryStream } from "../utils/binary-stream.js";
2
+ import { ACK_FLAG, NACK_FLAG } from "../constants.js";
3
+
4
+ export function encodeAck(sequences) { return encodeAckLike(ACK_FLAG, sequences); }
5
+ export function encodeNack(sequences) { return encodeAckLike(NACK_FLAG, sequences); }
6
+
7
+ function encodeAckLike(flag, sequences) {
8
+ const sorted = [...sequences].sort((a, b) => a - b);
9
+ const records = [];
10
+
11
+ for (const seq of sorted) {
12
+ if (records.length > 0) {
13
+ const last = records[records.length - 1];
14
+ if (seq === last.max + 1) { last.max = seq; continue; }
15
+ }
16
+ records.push({ min: seq, max: seq });
17
+ }
18
+
19
+ const s = new BinaryStream();
20
+ s.writeByte(flag);
21
+ s.writeUInt16BE(records.length);
22
+ for (const r of records) {
23
+ const single = r.min === r.max;
24
+ s.writeBool(single);
25
+ s.writeUInt24LE(r.min);
26
+ if (!single) s.writeUInt24LE(r.max);
27
+ }
28
+ return s.flush();
29
+ }
30
+
31
+ export function decodeAckLike(buf) {
32
+ const s = new BinaryStream(buf);
33
+ s.skip(1);
34
+ const count = s.readUInt16BE();
35
+ const sequences = [];
36
+ for (let i = 0; i < count; i++) {
37
+ const single = s.readBool();
38
+ const min = s.readUInt24LE();
39
+ if (single) {
40
+ sequences.push(min);
41
+ } else {
42
+ const max = s.readUInt24LE();
43
+ for (let j = min; j <= max; j++) sequences.push(j);
44
+ }
45
+ }
46
+ return sequences;
47
+ }
@@ -0,0 +1,100 @@
1
+ import { BinaryStream } from "../utils/binary-stream.js";
2
+ import { PacketReliability, isReliable, isSequenced, isOrdered } from "../constants.js";
3
+
4
+ export function createFrame(body, reliability = PacketReliability.ReliableOrdered, orderChannel = 0) {
5
+ return {
6
+ reliability,
7
+ reliableIndex : 0,
8
+ sequenceIndex : 0,
9
+ orderIndex : 0,
10
+ orderChannel,
11
+ split : false,
12
+ splitCount : 0,
13
+ splitId : 0,
14
+ splitIndex : 0,
15
+ body,
16
+ };
17
+ }
18
+
19
+ export function getFrameHeaderSize(frame) {
20
+ let size = 3;
21
+ if (isReliable(frame.reliability)) size += 3;
22
+ if (isSequenced(frame.reliability)) size += 3;
23
+ if (isOrdered(frame.reliability)) size += 4;
24
+ if (frame.split) size += 10;
25
+ return size;
26
+ }
27
+
28
+ export function getFrameSize(frame) {
29
+ return getFrameHeaderSize(frame) + frame.body.length;
30
+ }
31
+
32
+ export function writeFrame(stream, frame) {
33
+ let flags = (frame.reliability << 5) & 0xe0;
34
+ if (frame.split) flags |= 0x10;
35
+ stream.writeByte(flags);
36
+ stream.writeUInt16BE(frame.body.length << 3);
37
+
38
+ if (isReliable(frame.reliability)) stream.writeUInt24LE(frame.reliableIndex);
39
+ if (isSequenced(frame.reliability)) stream.writeUInt24LE(frame.sequenceIndex);
40
+ if (isOrdered(frame.reliability)) {
41
+ stream.writeUInt24LE(frame.orderIndex);
42
+ stream.writeByte(frame.orderChannel);
43
+ }
44
+ if (frame.split) {
45
+ stream.writeUInt32BE(frame.splitCount);
46
+ stream.writeUInt16BE(frame.splitId);
47
+ stream.writeUInt32BE(frame.splitIndex);
48
+ }
49
+
50
+ stream.writeBytes(frame.body);
51
+ }
52
+
53
+ export function readFrame(stream) {
54
+ const flags = stream.readByte();
55
+ const reliability = (flags & 0xe0) >> 5;
56
+ const hasSplit = (flags & 0x10) !== 0;
57
+
58
+ const bitLength = stream.readUInt16BE();
59
+ const byteLength = Math.ceil(bitLength / 8);
60
+
61
+ let reliableIndex = 0;
62
+ let sequenceIndex = 0;
63
+ let orderIndex = 0;
64
+ let orderChannel = 0;
65
+ let splitCount = 0;
66
+ let splitId = 0;
67
+ let splitIndex = 0;
68
+
69
+ if (isReliable(reliability)) reliableIndex = stream.readUInt24LE();
70
+ if (isSequenced(reliability)) sequenceIndex = stream.readUInt24LE();
71
+ if (isOrdered(reliability)) {
72
+ orderIndex = stream.readUInt24LE();
73
+ orderChannel = stream.readByte();
74
+ }
75
+ if (hasSplit) {
76
+ splitCount = stream.readUInt32BE();
77
+ splitId = stream.readUInt16BE();
78
+ splitIndex = stream.readUInt32BE();
79
+ }
80
+
81
+ const body = stream.readBytes(byteLength);
82
+
83
+ return { reliability, reliableIndex, sequenceIndex, orderIndex, orderChannel, split: hasSplit, splitCount, splitId, splitIndex, body };
84
+ }
85
+
86
+ export function readFrames(buf) {
87
+ const stream = new BinaryStream(buf);
88
+ stream.skip(4);
89
+ const frames = [];
90
+ while (stream.remaining > 0) frames.push(readFrame(stream));
91
+ return frames;
92
+ }
93
+
94
+ export function encodeFrameSet(sequenceNumber, frames) {
95
+ const stream = new BinaryStream();
96
+ stream.writeByte(0x84);
97
+ stream.writeUInt24LE(sequenceNumber);
98
+ for (const frame of frames) writeFrame(stream, frame);
99
+ return stream.flush();
100
+ }