@skating/swiftpacket 0.1.4

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,1606 @@
1
+ --!strict
2
+ --!optimize 2
3
+
4
+ --[[
5
+ Author: @skaterstudios
6
+ Website: skaterstudios.com
7
+ GitHub: github.com/skatingii/SwiftPacket
8
+ Discord Server: discord.gg/skaterstudios
9
+
10
+ Licensed under the MIT License. You may use, modify, redistribute and sell
11
+ this system freely, as long as this notice and the LICENSE file stay with it.
12
+ ]]
13
+
14
+ export type Cursor = {
15
+ Buffer: buffer,
16
+ Length: number,
17
+ Offset: number,
18
+ Instances: { any },
19
+ InstancesOffset: number,
20
+ }
21
+
22
+ local ActiveCursor: Cursor? = nil
23
+ local ActiveBuffer: buffer = buffer.create(0)
24
+ local BufferLength = 0
25
+ local BufferOffset = 0
26
+ local Instances: { any } = {}
27
+ local InstancesOffset = 0
28
+ local Untrusted = false
29
+ local Writing = "a value"
30
+
31
+ const function WriteFailure(Message: string): never
32
+ if Writing == "" then
33
+ error(`[swiftpacket] {Message}`, 0)
34
+ end
35
+
36
+ error(`[swiftpacket] could not write {Writing}: {Message}`, 0)
37
+ end
38
+
39
+ const function NewCursor(Size: number?): Cursor
40
+ const Length = Size or 128
41
+
42
+ return {
43
+ Buffer = buffer.create(Length),
44
+ Length = Length,
45
+ Offset = 0,
46
+ Instances = {},
47
+ InstancesOffset = 0,
48
+ }
49
+ end
50
+
51
+ const function Import(Target: Cursor)
52
+ ActiveCursor = Target
53
+ ActiveBuffer = Target.Buffer
54
+ BufferLength = Target.Length
55
+ BufferOffset = Target.Offset
56
+ Instances = Target.Instances
57
+ InstancesOffset = Target.InstancesOffset
58
+ Untrusted = false
59
+ end
60
+
61
+ const function Export(): Cursor
62
+ const Target = ActiveCursor
63
+
64
+ if Target == nil then
65
+ error("[swiftpacket] there is no cursor to export, the active one is read only", 0)
66
+ end
67
+
68
+ Target.Buffer = ActiveBuffer
69
+ Target.Length = BufferLength
70
+ Target.Offset = BufferOffset
71
+ Target.InstancesOffset = InstancesOffset
72
+
73
+ return Target
74
+ end
75
+
76
+ const function Load(
77
+ Source: buffer,
78
+ SourceInstances: { any },
79
+ IsUntrusted: boolean,
80
+ Offset: number,
81
+ StartInstances: number
82
+ )
83
+ ActiveCursor = nil
84
+ ActiveBuffer = Source
85
+ BufferLength = buffer.len(Source)
86
+ BufferOffset = Offset
87
+ Instances = SourceInstances
88
+ InstancesOffset = StartInstances
89
+ Untrusted = IsUntrusted
90
+ end
91
+
92
+ const function Reset(Target: Cursor)
93
+ Target.Offset = 0
94
+
95
+ if Target.InstancesOffset > 0 then
96
+ Target.InstancesOffset = 0
97
+ table.clear(Target.Instances)
98
+ end
99
+ end
100
+
101
+ const function Take(Target: Cursor): (buffer, { any }?)
102
+ const Truncated = buffer.create(Target.Offset)
103
+ buffer.copy(Truncated, 0, Target.Buffer, 0, Target.Offset)
104
+
105
+ if Target.InstancesOffset == 0 then
106
+ return Truncated, nil
107
+ end
108
+
109
+ return Truncated, table.move(Target.Instances, 1, Target.InstancesOffset, 1, table.create(Target.InstancesOffset))
110
+ end
111
+
112
+ const function Ended(): boolean
113
+ return BufferOffset >= BufferLength
114
+ end
115
+
116
+ const function Remaining(): number
117
+ return BufferLength - BufferOffset
118
+ end
119
+
120
+ const function RemainingInstances(): number
121
+ return #Instances - InstancesOffset
122
+ end
123
+
124
+ const function Allocate(Bytes: number)
125
+ const TargetLength = BufferOffset + Bytes
126
+
127
+ if BufferLength >= TargetLength then
128
+ return
129
+ end
130
+
131
+ while BufferLength < TargetLength do
132
+ BufferLength *= 2
133
+ end
134
+
135
+ const Grown = buffer.create(BufferLength)
136
+ buffer.copy(Grown, 0, ActiveBuffer, 0, BufferOffset)
137
+ ActiveBuffer = Grown
138
+ end
139
+
140
+ const function Append(Source: Cursor)
141
+ Allocate(Source.Offset)
142
+ buffer.copy(ActiveBuffer, BufferOffset, Source.Buffer, 0, Source.Offset)
143
+ BufferOffset += Source.Offset
144
+
145
+ for Index = 1, Source.InstancesOffset do
146
+ InstancesOffset += 1
147
+ Instances[InstancesOffset] = Source.Instances[Index]
148
+ end
149
+ end
150
+
151
+ const function CheckFloat(Value: number): number
152
+ if Untrusted and (Value ~= Value or Value == math.huge or Value == -math.huge) then
153
+ error("[swiftpacket] rejected a non-finite number", 0)
154
+ end
155
+
156
+ return Value
157
+ end
158
+
159
+ const function ReadU8(): number
160
+ const Value = buffer.readu8(ActiveBuffer, BufferOffset)
161
+ BufferOffset += 1
162
+
163
+ return Value
164
+ end
165
+
166
+ const function WriteU8(Value: number)
167
+ buffer.writeu8(ActiveBuffer, BufferOffset, Value)
168
+ BufferOffset += 1
169
+ end
170
+
171
+ const function ReadI8(): number
172
+ const Value = buffer.readi8(ActiveBuffer, BufferOffset)
173
+ BufferOffset += 1
174
+
175
+ return Value
176
+ end
177
+
178
+ const function WriteI8(Value: number)
179
+ buffer.writei8(ActiveBuffer, BufferOffset, Value)
180
+ BufferOffset += 1
181
+ end
182
+
183
+ const function ReadU16(): number
184
+ const Value = buffer.readu16(ActiveBuffer, BufferOffset)
185
+ BufferOffset += 2
186
+
187
+ return Value
188
+ end
189
+
190
+ const function WriteU16(Value: number)
191
+ buffer.writeu16(ActiveBuffer, BufferOffset, Value)
192
+ BufferOffset += 2
193
+ end
194
+
195
+ const function ReadI16(): number
196
+ const Value = buffer.readi16(ActiveBuffer, BufferOffset)
197
+ BufferOffset += 2
198
+
199
+ return Value
200
+ end
201
+
202
+ const function WriteI16(Value: number)
203
+ buffer.writei16(ActiveBuffer, BufferOffset, Value)
204
+ BufferOffset += 2
205
+ end
206
+
207
+ const function ReadU24(): number
208
+ const Value = buffer.readbits(ActiveBuffer, BufferOffset * 8, 24)
209
+ BufferOffset += 3
210
+
211
+ return Value
212
+ end
213
+
214
+ const function WriteU24(Value: number)
215
+ buffer.writebits(ActiveBuffer, BufferOffset * 8, 24, Value)
216
+ BufferOffset += 3
217
+ end
218
+
219
+ const function ReadI24(): number
220
+ const Value = buffer.readbits(ActiveBuffer, BufferOffset * 8, 24) - 8388608
221
+ BufferOffset += 3
222
+
223
+ return Value
224
+ end
225
+
226
+ const function WriteI24(Value: number)
227
+ buffer.writebits(ActiveBuffer, BufferOffset * 8, 24, Value + 8388608)
228
+ BufferOffset += 3
229
+ end
230
+
231
+ const function ReadU32(): number
232
+ const Value = buffer.readu32(ActiveBuffer, BufferOffset)
233
+ BufferOffset += 4
234
+
235
+ return Value
236
+ end
237
+
238
+ const function WriteU32(Value: number)
239
+ buffer.writeu32(ActiveBuffer, BufferOffset, Value)
240
+ BufferOffset += 4
241
+ end
242
+
243
+ const function ReadI32(): number
244
+ const Value = buffer.readi32(ActiveBuffer, BufferOffset)
245
+ BufferOffset += 4
246
+
247
+ return Value
248
+ end
249
+
250
+ const function WriteI32(Value: number)
251
+ buffer.writei32(ActiveBuffer, BufferOffset, Value)
252
+ BufferOffset += 4
253
+ end
254
+
255
+ const function ReadF32(): number
256
+ const Value = buffer.readf32(ActiveBuffer, BufferOffset)
257
+ BufferOffset += 4
258
+
259
+ return CheckFloat(Value)
260
+ end
261
+
262
+ const function WriteF32(Value: number)
263
+ buffer.writef32(ActiveBuffer, BufferOffset, Value)
264
+ BufferOffset += 4
265
+ end
266
+
267
+ const function ReadF64(): number
268
+ const Value = buffer.readf64(ActiveBuffer, BufferOffset)
269
+ BufferOffset += 8
270
+
271
+ return CheckFloat(Value)
272
+ end
273
+
274
+ const function WriteF64(Value: number)
275
+ buffer.writef64(ActiveBuffer, BufferOffset, Value)
276
+ BufferOffset += 8
277
+ end
278
+
279
+ const function ReadBits(BitOffset: number, Count: number): number
280
+ return buffer.readbits(ActiveBuffer, BufferOffset * 8 + BitOffset, Count)
281
+ end
282
+
283
+ const function WriteBits(BitOffset: number, Count: number, Value: number)
284
+ buffer.writebits(ActiveBuffer, BufferOffset * 8 + BitOffset, Count, Value)
285
+ end
286
+
287
+ const function Skip(Bytes: number)
288
+ BufferOffset += Bytes
289
+ end
290
+
291
+ const function ReadVarint(): number
292
+ local Value = 0
293
+ local Scale = 1
294
+
295
+ for Index = 1, 4 do
296
+ const Byte = buffer.readu8(ActiveBuffer, BufferOffset)
297
+ BufferOffset += 1
298
+
299
+ if Index == 4 or Byte < 128 then
300
+ return Value + Byte * Scale
301
+ end
302
+
303
+ Value += (Byte - 128) * Scale
304
+ Scale *= 128
305
+ end
306
+
307
+ return Value
308
+ end
309
+
310
+ const function WriteVarint(Value: number)
311
+ if Value < 0 or Value >= 268435456 or Value % 1 ~= 0 then
312
+ WriteFailure(`length {Value} must be a whole number from 0 to 268435455`)
313
+ end
314
+
315
+ local Left = Value
316
+
317
+ while Left >= 128 do
318
+ buffer.writeu8(ActiveBuffer, BufferOffset, Left % 128 + 128)
319
+ BufferOffset += 1
320
+ Left //= 128
321
+ end
322
+
323
+ buffer.writeu8(ActiveBuffer, BufferOffset, Left)
324
+ BufferOffset += 1
325
+ end
326
+
327
+ const function ReadString(Length: number): string
328
+ const Value = buffer.readstring(ActiveBuffer, BufferOffset, Length)
329
+ BufferOffset += Length
330
+
331
+ return Value
332
+ end
333
+
334
+ const function WriteString(Value: string)
335
+ buffer.writestring(ActiveBuffer, BufferOffset, Value)
336
+ BufferOffset += #Value
337
+ end
338
+
339
+ const function ReadBuffer(Length: number): buffer
340
+ const Value = buffer.create(Length)
341
+ buffer.copy(Value, 0, ActiveBuffer, BufferOffset, Length)
342
+ BufferOffset += Length
343
+
344
+ return Value
345
+ end
346
+
347
+ const function WriteBuffer(Value: buffer)
348
+ buffer.copy(ActiveBuffer, BufferOffset, Value)
349
+ BufferOffset += buffer.len(Value)
350
+ end
351
+
352
+ const function ReadInstance(): Instance?
353
+ InstancesOffset += 1
354
+
355
+ const Value = Instances[InstancesOffset]
356
+
357
+ if typeof(Value) == "Instance" then
358
+ return Value
359
+ end
360
+
361
+ return nil
362
+ end
363
+
364
+ const function WriteInstance(Value: Instance?)
365
+ InstancesOffset += 1
366
+ Instances[InstancesOffset] = if Value == nil then false else Value
367
+ end
368
+
369
+ const function WriteId(Id: number)
370
+ if Id < 128 then
371
+ Allocate(1)
372
+ buffer.writeu8(ActiveBuffer, BufferOffset, Id)
373
+ BufferOffset += 1
374
+ else
375
+ Allocate(4)
376
+ WriteVarint(Id)
377
+ end
378
+ end
379
+
380
+ const CursorLibrary = {
381
+ New = NewCursor,
382
+ Import = Import,
383
+ Export = Export,
384
+ Load = Load,
385
+ Reset = Reset,
386
+ Take = Take,
387
+ Ended = Ended,
388
+ Remaining = Remaining,
389
+ RemainingInstances = RemainingInstances,
390
+ Allocate = Allocate,
391
+ Append = Append,
392
+ CheckFloat = CheckFloat,
393
+ ReadU8 = ReadU8,
394
+ WriteU8 = WriteU8,
395
+ ReadI8 = ReadI8,
396
+ WriteI8 = WriteI8,
397
+ ReadU16 = ReadU16,
398
+ WriteU16 = WriteU16,
399
+ ReadI16 = ReadI16,
400
+ WriteI16 = WriteI16,
401
+ ReadU24 = ReadU24,
402
+ WriteU24 = WriteU24,
403
+ ReadI24 = ReadI24,
404
+ WriteI24 = WriteI24,
405
+ ReadU32 = ReadU32,
406
+ WriteU32 = WriteU32,
407
+ ReadI32 = ReadI32,
408
+ WriteI32 = WriteI32,
409
+ ReadF32 = ReadF32,
410
+ WriteF32 = WriteF32,
411
+ ReadF64 = ReadF64,
412
+ WriteF64 = WriteF64,
413
+ ReadBits = ReadBits,
414
+ WriteBits = WriteBits,
415
+ Skip = Skip,
416
+ ReadVarint = ReadVarint,
417
+ WriteVarint = WriteVarint,
418
+ WriteId = WriteId,
419
+ ReadString = ReadString,
420
+ WriteString = WriteString,
421
+ ReadBuffer = ReadBuffer,
422
+ WriteBuffer = WriteBuffer,
423
+ ReadInstance = ReadInstance,
424
+ WriteInstance = WriteInstance,
425
+ }
426
+
427
+ function CursorLibrary.Truncate(): (buffer, { any }?)
428
+ return Take(Export())
429
+ end
430
+
431
+ function CursorLibrary.Position(): (number, number)
432
+ return BufferOffset, InstancesOffset
433
+ end
434
+
435
+ export type Schema = {
436
+ Read: () -> any,
437
+ Write: (Value: any) -> (),
438
+ MinimumSize: number,
439
+ }
440
+
441
+ export type Arguments = { n: number, [number]: any }
442
+
443
+ const Schema = {}
444
+
445
+ const Registered: { [any]: boolean } = setmetatable({}, { __mode = "k" })
446
+
447
+ function Schema.Define(Read: () -> any, Write: (Value: any) -> (), MinimumSize: number): Schema
448
+ const Definition: Schema = {
449
+ Read = Read,
450
+ Write = Write,
451
+ MinimumSize = MinimumSize,
452
+ }
453
+
454
+ Registered[Definition] = true
455
+
456
+ return Definition
457
+ end
458
+
459
+ function Schema.Is(Value: any): boolean
460
+ return Registered[Value] == true
461
+ end
462
+
463
+ const Define = Schema.Define
464
+
465
+ const function CheckCount(Count: number, MinimumSize: number, MaximumCount: number?)
466
+ if MaximumCount and Count > MaximumCount then
467
+ error(`[swiftpacket] count {Count} is over the maximum of {MaximumCount}`, 0)
468
+ end
469
+
470
+ if MinimumSize > 0 then
471
+ if Count * MinimumSize > Remaining() then
472
+ error(`[swiftpacket] count {Count} is larger than the data left in the batch`, 0)
473
+ end
474
+ elseif Count > Remaining() + RemainingInstances() then
475
+ error(`[swiftpacket] count {Count} is larger than the data left in the batch`, 0)
476
+ end
477
+ end
478
+
479
+ const function CheckLength(Length: number, MaximumLength: number?)
480
+ if MaximumLength and Length > MaximumLength then
481
+ error(`[swiftpacket] length {Length} is over the maximum of {MaximumLength}`, 0)
482
+ end
483
+
484
+ if Length > Remaining() then
485
+ error(`[swiftpacket] length {Length} is larger than the data left in the batch`, 0)
486
+ end
487
+ end
488
+
489
+ const function CountEntries(Value: { [any]: any }): number
490
+ local Count = 0
491
+
492
+ for Key in Value do
493
+ Count += 1
494
+ end
495
+
496
+ return Count
497
+ end
498
+
499
+ const function FloatCodec(ExponentBits: number, MantissaBits: number)
500
+ const Bias = 2 ^ (ExponentBits - 1) - 1
501
+ const MaximumExponent = 2 ^ ExponentBits - 1
502
+ const MantissaScale = 2 ^ MantissaBits
503
+ const SignScale = 2 ^ (ExponentBits + MantissaBits)
504
+ const SubnormalScale = 2 ^ (1 - Bias - MantissaBits)
505
+
506
+ const function Encode(Value: number): number
507
+ if Value ~= Value then
508
+ return MaximumExponent * MantissaScale + 1
509
+ end
510
+
511
+ local Sign = 0
512
+ local Magnitude = Value
513
+
514
+ if Magnitude < 0 or (Magnitude == 0 and 1 / Magnitude < 0) then
515
+ Sign = SignScale
516
+ Magnitude = -Magnitude
517
+ end
518
+
519
+ if Magnitude == 0 then
520
+ return Sign
521
+ end
522
+
523
+ if Magnitude == math.huge then
524
+ return Sign + MaximumExponent * MantissaScale
525
+ end
526
+
527
+ const Mantissa, Exponent = math.frexp(Magnitude)
528
+ local Biased = Exponent - 1 + Bias
529
+ local Fraction: number
530
+
531
+ if Biased <= 0 then
532
+ Fraction = math.round(Magnitude / SubnormalScale)
533
+ Biased = 0
534
+
535
+ if Fraction >= MantissaScale then
536
+ Fraction = 0
537
+ Biased = 1
538
+ end
539
+ else
540
+ Fraction = math.round((Mantissa * 2 - 1) * MantissaScale)
541
+
542
+ if Fraction >= MantissaScale then
543
+ Fraction = 0
544
+ Biased += 1
545
+ end
546
+ end
547
+
548
+ if Biased >= MaximumExponent then
549
+ return Sign + MaximumExponent * MantissaScale
550
+ end
551
+
552
+ return Sign + Biased * MantissaScale + Fraction
553
+ end
554
+
555
+ const function Decode(Bits: number): number
556
+ const Fraction = Bits % MantissaScale
557
+ const Biased = (Bits // MantissaScale) % (MaximumExponent + 1)
558
+ const Sign = if Bits >= SignScale then -1 else 1
559
+
560
+ if Biased == MaximumExponent then
561
+ return CheckFloat(if Fraction == 0 then Sign * math.huge else 0 / 0)
562
+ end
563
+
564
+ if Biased == 0 then
565
+ return Sign * Fraction * SubnormalScale
566
+ end
567
+
568
+ return Sign * (1 + Fraction / MantissaScale) * 2 ^ (Biased - Bias)
569
+ end
570
+
571
+ return Encode, Decode
572
+ end
573
+
574
+ const EncodeF16, DecodeF16 = FloatCodec(5, 10)
575
+ const EncodeF24, DecodeF24 = FloatCodec(6, 17)
576
+
577
+ const function ReadF16(): number
578
+ return DecodeF16(ReadU16())
579
+ end
580
+
581
+ const function WriteF16(Value: number)
582
+ WriteU16(EncodeF16(Value))
583
+ end
584
+
585
+ const function ReadF24(): number
586
+ return DecodeF24(ReadU24())
587
+ end
588
+
589
+ const function WriteF24(Value: number)
590
+ WriteU24(EncodeF24(Value))
591
+ end
592
+
593
+ const RotationScale = 65535 / math.sqrt(2)
594
+ const RotationOffset = 32767.5
595
+
596
+ const function WriteRotation(Value: CFrame)
597
+ const Axis, Angle = Value:ToAxisAngle()
598
+ const Sine = math.sin(Angle / 2)
599
+ const Components = { Axis.X * Sine, Axis.Y * Sine, Axis.Z * Sine, math.cos(Angle / 2) }
600
+
601
+ local Largest = 1
602
+
603
+ for Index = 2, 4 do
604
+ if math.abs(Components[Index]) > math.abs(Components[Largest]) then
605
+ Largest = Index
606
+ end
607
+ end
608
+
609
+ const Flip = if Components[Largest] < 0 then -1 else 1
610
+
611
+ WriteU8(Largest - 1)
612
+
613
+ for Index = 1, 4 do
614
+ if Index ~= Largest then
615
+ WriteU16(math.clamp(math.round(Components[Index] * Flip * RotationScale + RotationOffset), 0, 65535))
616
+ end
617
+ end
618
+ end
619
+
620
+ const function ReadRotation(): (number, number, number, number)
621
+ const Largest = ReadU8() + 1
622
+
623
+ if Largest > 4 then
624
+ error("[swiftpacket] rejected an invalid rotation", 0)
625
+ end
626
+
627
+ const Components = { 0, 0, 0, 0 }
628
+ local Sum = 0
629
+
630
+ for Index = 1, 4 do
631
+ if Index ~= Largest then
632
+ const Component = (ReadU16() - RotationOffset) / RotationScale
633
+ Components[Index] = Component
634
+ Sum += Component * Component
635
+ end
636
+ end
637
+
638
+ Components[Largest] = math.sqrt(math.max(0, 1 - Sum))
639
+
640
+ return Components[1], Components[2], Components[3], Components[4]
641
+ end
642
+
643
+ const function WriteNumberSequence(Value: NumberSequence)
644
+ const Keypoints = Value.Keypoints
645
+
646
+ Allocate(4 + #Keypoints * 12)
647
+ WriteVarint(#Keypoints)
648
+
649
+ for Index = 1, #Keypoints do
650
+ const Keypoint = Keypoints[Index]
651
+
652
+ WriteF32(Keypoint.Time)
653
+ WriteF32(Keypoint.Value)
654
+ WriteF32(Keypoint.Envelope)
655
+ end
656
+ end
657
+
658
+ const function ReadNumberSequence(): NumberSequence
659
+ const Count = ReadVarint()
660
+ CheckCount(Count, 12, 20)
661
+
662
+ const Keypoints: { NumberSequenceKeypoint } = {}
663
+
664
+ for Index = 1, Count do
665
+ Keypoints[Index] = NumberSequenceKeypoint.new(ReadF32(), ReadF32(), ReadF32())
666
+ end
667
+
668
+ return NumberSequence.new(Keypoints)
669
+ end
670
+
671
+ const function WriteColorSequence(Value: ColorSequence)
672
+ const Keypoints = Value.Keypoints
673
+
674
+ Allocate(4 + #Keypoints * 7)
675
+ WriteVarint(#Keypoints)
676
+
677
+ for Index = 1, #Keypoints do
678
+ const Keypoint = Keypoints[Index]
679
+
680
+ WriteF32(Keypoint.Time)
681
+ WriteU8(math.round(Keypoint.Value.R * 255))
682
+ WriteU8(math.round(Keypoint.Value.G * 255))
683
+ WriteU8(math.round(Keypoint.Value.B * 255))
684
+ end
685
+ end
686
+
687
+ const function ReadColorSequence(): ColorSequence
688
+ const Count = ReadVarint()
689
+ CheckCount(Count, 7, 20)
690
+
691
+ const Keypoints: { ColorSequenceKeypoint } = {}
692
+
693
+ for Index = 1, Count do
694
+ Keypoints[Index] = ColorSequenceKeypoint.new(ReadF32(), Color3.fromRGB(ReadU8(), ReadU8(), ReadU8()))
695
+ end
696
+
697
+ return ColorSequence.new(Keypoints)
698
+ end
699
+
700
+ Schema.Nil = Define(function()
701
+ return nil
702
+ end, function() end, 0)
703
+
704
+ Schema.Boolean = Define(function()
705
+ return ReadU8() ~= 0
706
+ end, function(Value: boolean)
707
+ Allocate(1)
708
+ WriteU8(if Value then 1 else 0)
709
+ end, 1)
710
+
711
+ Schema.U8 = Define(ReadU8, function(Value: number)
712
+ Allocate(1)
713
+ WriteU8(Value)
714
+ end, 1)
715
+
716
+ Schema.U16 = Define(ReadU16, function(Value: number)
717
+ Allocate(2)
718
+ WriteU16(Value)
719
+ end, 2)
720
+
721
+ Schema.U24 = Define(ReadU24, function(Value: number)
722
+ Allocate(3)
723
+ WriteU24(Value)
724
+ end, 3)
725
+
726
+ Schema.U32 = Define(ReadU32, function(Value: number)
727
+ Allocate(4)
728
+ WriteU32(Value)
729
+ end, 4)
730
+
731
+ Schema.I8 = Define(ReadI8, function(Value: number)
732
+ Allocate(1)
733
+ WriteI8(Value)
734
+ end, 1)
735
+
736
+ Schema.I16 = Define(ReadI16, function(Value: number)
737
+ Allocate(2)
738
+ WriteI16(Value)
739
+ end, 2)
740
+
741
+ Schema.I24 = Define(ReadI24, function(Value: number)
742
+ Allocate(3)
743
+ WriteI24(Value)
744
+ end, 3)
745
+
746
+ Schema.I32 = Define(ReadI32, function(Value: number)
747
+ Allocate(4)
748
+ WriteI32(Value)
749
+ end, 4)
750
+
751
+ Schema.F16 = Define(ReadF16, function(Value: number)
752
+ Allocate(2)
753
+ WriteF16(Value)
754
+ end, 2)
755
+
756
+ Schema.F24 = Define(ReadF24, function(Value: number)
757
+ Allocate(3)
758
+ WriteF24(Value)
759
+ end, 3)
760
+
761
+ Schema.F32 = Define(ReadF32, function(Value: number)
762
+ Allocate(4)
763
+ WriteF32(Value)
764
+ end, 4)
765
+
766
+ Schema.F64 = Define(ReadF64, function(Value: number)
767
+ Allocate(8)
768
+ WriteF64(Value)
769
+ end, 8)
770
+
771
+ Schema.String = Define(function()
772
+ const Length = ReadVarint()
773
+ CheckLength(Length)
774
+
775
+ return ReadString(Length)
776
+ end, function(Value: string)
777
+ Allocate(4 + #Value)
778
+ WriteVarint(#Value)
779
+ WriteString(Value)
780
+ end, 1)
781
+
782
+ Schema.Buffer = Define(function()
783
+ const Length = ReadVarint()
784
+ CheckLength(Length)
785
+
786
+ return ReadBuffer(Length)
787
+ end, function(Value: buffer)
788
+ Allocate(4 + buffer.len(Value))
789
+ WriteVarint(buffer.len(Value))
790
+ WriteBuffer(Value)
791
+ end, 1)
792
+
793
+ Schema.Instance = Define(ReadInstance, WriteInstance, 0)
794
+
795
+ Schema.Vector2 = Define(function()
796
+ return Vector2.new(ReadF32(), ReadF32())
797
+ end, function(Value: Vector2)
798
+ Allocate(8)
799
+ WriteF32(Value.X)
800
+ WriteF32(Value.Y)
801
+ end, 8)
802
+
803
+ Schema.Vector2F16 = Define(function()
804
+ return Vector2.new(ReadF16(), ReadF16())
805
+ end, function(Value: Vector2)
806
+ Allocate(4)
807
+ WriteF16(Value.X)
808
+ WriteF16(Value.Y)
809
+ end, 4)
810
+
811
+ Schema.Vector2I16 = Define(function()
812
+ return Vector2.new(ReadI16(), ReadI16())
813
+ end, function(Value: Vector2)
814
+ Allocate(4)
815
+ WriteI16(math.round(Value.X))
816
+ WriteI16(math.round(Value.Y))
817
+ end, 4)
818
+
819
+ Schema.Vector3 = Define(function()
820
+ return Vector3.new(ReadF32(), ReadF32(), ReadF32())
821
+ end, function(Value: Vector3)
822
+ Allocate(12)
823
+ WriteF32(Value.X)
824
+ WriteF32(Value.Y)
825
+ WriteF32(Value.Z)
826
+ end, 12)
827
+
828
+ Schema.Vector3F24 = Define(function()
829
+ return Vector3.new(ReadF24(), ReadF24(), ReadF24())
830
+ end, function(Value: Vector3)
831
+ Allocate(9)
832
+ WriteF24(Value.X)
833
+ WriteF24(Value.Y)
834
+ WriteF24(Value.Z)
835
+ end, 9)
836
+
837
+ Schema.Vector3F16 = Define(function()
838
+ return Vector3.new(ReadF16(), ReadF16(), ReadF16())
839
+ end, function(Value: Vector3)
840
+ Allocate(6)
841
+ WriteF16(Value.X)
842
+ WriteF16(Value.Y)
843
+ WriteF16(Value.Z)
844
+ end, 6)
845
+
846
+ Schema.Vector3I16 = Define(function()
847
+ return Vector3.new(ReadI16(), ReadI16(), ReadI16())
848
+ end, function(Value: Vector3)
849
+ Allocate(6)
850
+ WriteI16(math.round(Value.X))
851
+ WriteI16(math.round(Value.Y))
852
+ WriteI16(math.round(Value.Z))
853
+ end, 6)
854
+
855
+ Schema.CFrame = Define(function()
856
+ const X, Y, Z = ReadF32(), ReadF32(), ReadF32()
857
+ const QuaternionX, QuaternionY, QuaternionZ, QuaternionW = ReadRotation()
858
+
859
+ return CFrame.new(X, Y, Z, QuaternionX, QuaternionY, QuaternionZ, QuaternionW)
860
+ end, function(Value: CFrame)
861
+ Allocate(19)
862
+ WriteF32(Value.X)
863
+ WriteF32(Value.Y)
864
+ WriteF32(Value.Z)
865
+ WriteRotation(Value)
866
+ end, 19)
867
+
868
+ Schema.CFrameF24 = Define(function()
869
+ const X, Y, Z = ReadF24(), ReadF24(), ReadF24()
870
+ const QuaternionX, QuaternionY, QuaternionZ, QuaternionW = ReadRotation()
871
+
872
+ return CFrame.new(X, Y, Z, QuaternionX, QuaternionY, QuaternionZ, QuaternionW)
873
+ end, function(Value: CFrame)
874
+ Allocate(16)
875
+ WriteF24(Value.X)
876
+ WriteF24(Value.Y)
877
+ WriteF24(Value.Z)
878
+ WriteRotation(Value)
879
+ end, 16)
880
+
881
+ Schema.Color3 = Define(function()
882
+ return Color3.fromRGB(ReadU8(), ReadU8(), ReadU8())
883
+ end, function(Value: Color3)
884
+ Allocate(3)
885
+ WriteU8(math.round(math.clamp(Value.R, 0, 1) * 255))
886
+ WriteU8(math.round(math.clamp(Value.G, 0, 1) * 255))
887
+ WriteU8(math.round(math.clamp(Value.B, 0, 1) * 255))
888
+ end, 3)
889
+
890
+ Schema.BrickColor = Define(function()
891
+ return BrickColor.new(ReadU16())
892
+ end, function(Value: BrickColor)
893
+ Allocate(2)
894
+ WriteU16(Value.Number)
895
+ end, 2)
896
+
897
+ Schema.UDim = Define(function()
898
+ return UDim.new(ReadF32(), ReadI32())
899
+ end, function(Value: UDim)
900
+ Allocate(8)
901
+ WriteF32(Value.Scale)
902
+ WriteI32(Value.Offset)
903
+ end, 8)
904
+
905
+ Schema.UDim2 = Define(function()
906
+ return UDim2.new(ReadF32(), ReadI32(), ReadF32(), ReadI32())
907
+ end, function(Value: UDim2)
908
+ Allocate(16)
909
+ WriteF32(Value.X.Scale)
910
+ WriteI32(Value.X.Offset)
911
+ WriteF32(Value.Y.Scale)
912
+ WriteI32(Value.Y.Offset)
913
+ end, 16)
914
+
915
+ Schema.Rect = Define(function()
916
+ return Rect.new(ReadF32(), ReadF32(), ReadF32(), ReadF32())
917
+ end, function(Value: Rect)
918
+ Allocate(16)
919
+ WriteF32(Value.Min.X)
920
+ WriteF32(Value.Min.Y)
921
+ WriteF32(Value.Max.X)
922
+ WriteF32(Value.Max.Y)
923
+ end, 16)
924
+
925
+ Schema.NumberRange = Define(function()
926
+ return NumberRange.new(ReadF32(), ReadF32())
927
+ end, function(Value: NumberRange)
928
+ Allocate(8)
929
+ WriteF32(Value.Min)
930
+ WriteF32(Value.Max)
931
+ end, 8)
932
+
933
+ Schema.NumberSequence = Define(ReadNumberSequence, WriteNumberSequence, 1)
934
+
935
+ Schema.ColorSequence = Define(ReadColorSequence, WriteColorSequence, 1)
936
+
937
+ Schema.DateTime = Define(function()
938
+ return DateTime.fromUnixTimestampMillis(ReadF64())
939
+ end, function(Value: DateTime)
940
+ Allocate(8)
941
+ WriteF64(Value.UnixTimestampMillis)
942
+ end, 8)
943
+
944
+ const AnyReads: { [number]: (Depth: number) -> any } = {}
945
+ const AnyWrites: { [string]: (Value: any, Depth: number) -> () } = {}
946
+ local EnumsByName: { [string]: Enum }? = nil
947
+
948
+ const function ReadAnyAt(Depth: number): any
949
+ const Tag = ReadU8()
950
+ const Reader = AnyReads[Tag]
951
+
952
+ if Reader == nil then
953
+ error(`[swiftpacket] rejected an unknown value tag {Tag}`, 0)
954
+ end
955
+
956
+ return Reader(Depth)
957
+ end
958
+
959
+ const function WriteAnyAt(Value: any, Depth: number)
960
+ const Kind = typeof(Value)
961
+ const Writer = AnyWrites[Kind]
962
+
963
+ if Writer == nil then
964
+ WriteFailure(`cannot send a value of type {Kind}`)
965
+ end
966
+
967
+ Writer(Value, Depth)
968
+ end
969
+
970
+ const function WriteTag(Tag: number, Bytes: number)
971
+ Allocate(1 + Bytes)
972
+ WriteU8(Tag)
973
+ end
974
+
975
+ const function BuildEnums(): { [string]: Enum }
976
+ const Built: { [string]: Enum } = {}
977
+ const Enums = Enum:GetEnums()
978
+
979
+ for Index = 1, #Enums do
980
+ Built[tostring(Enums[Index])] = Enums[Index]
981
+ end
982
+
983
+ return Built
984
+ end
985
+
986
+ const function FindEnum(Name: string): Enum
987
+ const Lookup = EnumsByName or BuildEnums()
988
+ EnumsByName = Lookup
989
+
990
+ const EnumType = Lookup[Name]
991
+
992
+ if EnumType == nil then
993
+ error(`[swiftpacket] rejected an unknown enum type {Name}`, 0)
994
+ end
995
+
996
+ return EnumType
997
+ end
998
+
999
+ AnyReads[0] = function()
1000
+ return nil
1001
+ end
1002
+
1003
+ AnyWrites["nil"] = function()
1004
+ WriteTag(0, 0)
1005
+ end
1006
+
1007
+ AnyReads[1] = function()
1008
+ return false
1009
+ end
1010
+
1011
+ AnyReads[2] = function()
1012
+ return true
1013
+ end
1014
+
1015
+ AnyWrites.boolean = function(Value: boolean)
1016
+ WriteTag(if Value then 2 else 1, 0)
1017
+ end
1018
+
1019
+ AnyReads[3] = ReadU8
1020
+ AnyReads[4] = ReadU16
1021
+ AnyReads[5] = ReadU32
1022
+
1023
+ AnyReads[6] = function()
1024
+ return -ReadU8()
1025
+ end
1026
+
1027
+ AnyReads[7] = function()
1028
+ return -ReadU16()
1029
+ end
1030
+
1031
+ AnyReads[8] = function()
1032
+ return -ReadU32()
1033
+ end
1034
+
1035
+ AnyReads[9] = ReadF64
1036
+
1037
+ AnyWrites.number = function(Value: number)
1038
+ if Value % 1 ~= 0 or math.abs(Value) >= 4294967296 then
1039
+ WriteTag(9, 8)
1040
+ WriteF64(Value)
1041
+ elseif Value >= 0 then
1042
+ if Value < 256 then
1043
+ WriteTag(3, 1)
1044
+ WriteU8(Value)
1045
+ elseif Value < 65536 then
1046
+ WriteTag(4, 2)
1047
+ WriteU16(Value)
1048
+ else
1049
+ WriteTag(5, 4)
1050
+ WriteU32(Value)
1051
+ end
1052
+ else
1053
+ const Magnitude = -Value
1054
+
1055
+ if Magnitude < 256 then
1056
+ WriteTag(6, 1)
1057
+ WriteU8(Magnitude)
1058
+ elseif Magnitude < 65536 then
1059
+ WriteTag(7, 2)
1060
+ WriteU16(Magnitude)
1061
+ else
1062
+ WriteTag(8, 4)
1063
+ WriteU32(Magnitude)
1064
+ end
1065
+ end
1066
+ end
1067
+
1068
+ const function AnyDatatype(Tag: number, Kind: string, Datatype: Schema)
1069
+ const Read, Write = Datatype.Read, Datatype.Write
1070
+
1071
+ AnyReads[Tag] = function()
1072
+ return Read()
1073
+ end
1074
+
1075
+ AnyWrites[Kind] = function(Value: any)
1076
+ WriteTag(Tag, 0)
1077
+ Write(Value)
1078
+ end
1079
+ end
1080
+
1081
+ AnyDatatype(10, "string", Schema.String)
1082
+ AnyDatatype(11, "buffer", Schema.Buffer)
1083
+ AnyDatatype(12, "Instance", Schema.Instance)
1084
+ AnyDatatype(13, "Vector2", Schema.Vector2)
1085
+ AnyDatatype(14, "Vector3", Schema.Vector3)
1086
+ AnyDatatype(15, "CFrame", Schema.CFrame)
1087
+ AnyDatatype(16, "Color3", Schema.Color3)
1088
+ AnyDatatype(17, "BrickColor", Schema.BrickColor)
1089
+ AnyDatatype(18, "UDim", Schema.UDim)
1090
+ AnyDatatype(19, "UDim2", Schema.UDim2)
1091
+ AnyDatatype(20, "Rect", Schema.Rect)
1092
+ AnyDatatype(21, "NumberRange", Schema.NumberRange)
1093
+ AnyDatatype(22, "NumberSequence", Schema.NumberSequence)
1094
+ AnyDatatype(23, "ColorSequence", Schema.ColorSequence)
1095
+ AnyDatatype(24, "DateTime", Schema.DateTime)
1096
+
1097
+ AnyReads[25] = function()
1098
+ const EnumType = FindEnum(Schema.String.Read())
1099
+ const Item = EnumType:FromValue(ReadU16())
1100
+
1101
+ if Item == nil then
1102
+ error(`[swiftpacket] rejected an unknown {tostring(EnumType)} value`, 0)
1103
+ end
1104
+
1105
+ return Item
1106
+ end
1107
+
1108
+ AnyWrites.EnumItem = function(Value: EnumItem)
1109
+ WriteTag(25, 0)
1110
+ Schema.String.Write(tostring(Value.EnumType))
1111
+ Allocate(2)
1112
+ WriteU16(Value.Value)
1113
+ end
1114
+
1115
+ AnyReads[26] = function(Depth: number)
1116
+ if Depth >= 16 then
1117
+ error("[swiftpacket] table nesting is deeper than 16 levels", 0)
1118
+ end
1119
+
1120
+ const Count = ReadVarint()
1121
+ CheckCount(Count, 2)
1122
+
1123
+ const Value = {}
1124
+
1125
+ for Index = 1, Count do
1126
+ const Key = ReadAnyAt(Depth + 1)
1127
+ const Entry = ReadAnyAt(Depth + 1)
1128
+
1129
+ if Key == nil or Key ~= Key then
1130
+ error(`[swiftpacket] rejected an invalid key in table entry {Index}`, 0)
1131
+ end
1132
+
1133
+ Value[Key] = Entry
1134
+ end
1135
+
1136
+ return Value
1137
+ end
1138
+
1139
+ AnyWrites.table = function(Value: { [any]: any }, Depth: number)
1140
+ if Depth >= 16 then
1141
+ WriteFailure("table nesting is deeper than 16 levels")
1142
+ end
1143
+
1144
+ WriteTag(26, 4)
1145
+ WriteVarint(CountEntries(Value))
1146
+
1147
+ for Key, Entry in Value do
1148
+ WriteAnyAt(Key, Depth + 1)
1149
+ WriteAnyAt(Entry, Depth + 1)
1150
+ end
1151
+ end
1152
+
1153
+ Schema.Any = Define(function()
1154
+ return ReadAnyAt(0)
1155
+ end, function(Value: any)
1156
+ WriteAnyAt(Value, 0)
1157
+ end, 1)
1158
+
1159
+ function Schema.Charset(Characters: string): Schema
1160
+ if #Characters < 2 or #Characters > 256 then
1161
+ error("[swiftpacket] a charset needs from 2 to 256 characters", 2)
1162
+ end
1163
+
1164
+ const Bits = math.ceil(math.log(#Characters, 2))
1165
+ const Lookup: { [string]: number } = {}
1166
+ const Alphabet: { string } = {}
1167
+
1168
+ for Index = 1, #Characters do
1169
+ const Character = string.sub(Characters, Index, Index)
1170
+
1171
+ if Lookup[Character] then
1172
+ error(`[swiftpacket] charset repeats the character "{Character}"`, 2)
1173
+ end
1174
+
1175
+ Lookup[Character] = Index - 1
1176
+ Alphabet[Index] = Character
1177
+ end
1178
+
1179
+ return Define(function()
1180
+ const Length = ReadVarint()
1181
+ const Bytes = math.ceil(Length * Bits / 8)
1182
+ CheckLength(Bytes)
1183
+
1184
+ const Output: { string } = {}
1185
+
1186
+ for Index = 1, Length do
1187
+ const Character = Alphabet[ReadBits((Index - 1) * Bits, Bits) + 1]
1188
+
1189
+ if Character == nil then
1190
+ error("[swiftpacket] rejected a character outside the charset", 0)
1191
+ end
1192
+
1193
+ Output[Index] = Character
1194
+ end
1195
+
1196
+ Skip(Bytes)
1197
+
1198
+ return table.concat(Output)
1199
+ end, function(Value: string)
1200
+ const Length = #Value
1201
+ const Bytes = math.ceil(Length * Bits / 8)
1202
+
1203
+ Allocate(4 + Bytes)
1204
+ WriteVarint(Length)
1205
+
1206
+ for Index = 1, Length do
1207
+ const Character = string.sub(Value, Index, Index)
1208
+ const Code = Lookup[Character]
1209
+
1210
+ if Code == nil then
1211
+ WriteFailure(`"{Character}" is not in the charset`)
1212
+ end
1213
+
1214
+ WriteBits((Index - 1) * Bits, Bits, Code)
1215
+ end
1216
+
1217
+ Skip(Bytes)
1218
+ end, 1)
1219
+ end
1220
+
1221
+ Schema.Characters = Schema.Charset(" .0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
1222
+
1223
+ const function Compile(Parameter: any): Schema
1224
+ if Registered[Parameter] then
1225
+ return Parameter
1226
+ end
1227
+
1228
+ if type(Parameter) ~= "table" then
1229
+ error(`[swiftpacket] {tostring(Parameter)} is not a swiftpacket type`, 0)
1230
+ end
1231
+
1232
+ if #Parameter == 1 and next(Parameter, 1) == nil then
1233
+ return Schema.Array(Parameter[1])
1234
+ end
1235
+
1236
+ return Schema.Struct(Parameter)
1237
+ end
1238
+
1239
+ Schema.Compile = Compile
1240
+
1241
+ function Schema.Array(Element: any, MaximumLength: number?): Schema
1242
+ const Item = Compile(Element)
1243
+ const ReadItem, WriteItem = Item.Read, Item.Write
1244
+ const ItemSize = Item.MinimumSize
1245
+
1246
+ return Define(function()
1247
+ const Length = ReadVarint()
1248
+ CheckCount(Length, ItemSize, MaximumLength)
1249
+
1250
+ const Values = {}
1251
+
1252
+ for Index = 1, Length do
1253
+ Values[Index] = ReadItem()
1254
+ end
1255
+
1256
+ return Values
1257
+ end, function(Values: { any })
1258
+ const Length = #Values
1259
+
1260
+ if MaximumLength and Length > MaximumLength then
1261
+ WriteFailure(`array length {Length} is over the maximum of {MaximumLength}`)
1262
+ end
1263
+
1264
+ Allocate(4)
1265
+ WriteVarint(Length)
1266
+
1267
+ for Index = 1, Length do
1268
+ WriteItem(Values[Index])
1269
+ end
1270
+ end, 1)
1271
+ end
1272
+
1273
+ function Schema.Map(Key: any, Value: any, MaximumCount: number?): Schema
1274
+ const KeySchema, ValueSchema = Compile(Key), Compile(Value)
1275
+ const ReadKey, WriteKey = KeySchema.Read, KeySchema.Write
1276
+ const ReadValue, WriteValue = ValueSchema.Read, ValueSchema.Write
1277
+ const EntrySize = KeySchema.MinimumSize + ValueSchema.MinimumSize
1278
+
1279
+ return Define(function()
1280
+ const Count = ReadVarint()
1281
+ CheckCount(Count, EntrySize, MaximumCount)
1282
+
1283
+ const Values = {}
1284
+
1285
+ for Index = 1, Count do
1286
+ const EntryKey = ReadKey()
1287
+ const EntryValue = ReadValue()
1288
+
1289
+ if EntryKey == nil or EntryKey ~= EntryKey then
1290
+ error(`[swiftpacket] rejected an invalid key in map entry {Index}`, 0)
1291
+ end
1292
+
1293
+ Values[EntryKey] = EntryValue
1294
+ end
1295
+
1296
+ return Values
1297
+ end, function(Values: { [any]: any })
1298
+ const Count = CountEntries(Values)
1299
+
1300
+ if MaximumCount and Count > MaximumCount then
1301
+ WriteFailure(`map count {Count} is over the maximum of {MaximumCount}`)
1302
+ end
1303
+
1304
+ Allocate(4)
1305
+ WriteVarint(Count)
1306
+
1307
+ for EntryKey, EntryValue in Values do
1308
+ WriteKey(EntryKey)
1309
+ WriteValue(EntryValue)
1310
+ end
1311
+ end, 1)
1312
+ end
1313
+
1314
+ function Schema.Struct(Shape: { [any]: any }): Schema
1315
+ const Keys: { string } = {}
1316
+
1317
+ for Key in Shape do
1318
+ if type(Key) ~= "string" then
1319
+ error(`[swiftpacket] struct key {tostring(Key)} must be a string`, 0)
1320
+ end
1321
+
1322
+ table.insert(Keys, Key)
1323
+ end
1324
+
1325
+ table.sort(Keys)
1326
+
1327
+ const Readers: { () -> any } = {}
1328
+ const Writers: { (Value: any) -> () } = {}
1329
+ local MinimumSize = 0
1330
+
1331
+ for Index, Key in Keys do
1332
+ const Field = Compile(Shape[Key])
1333
+ Readers[Index] = Field.Read
1334
+ Writers[Index] = Field.Write
1335
+ MinimumSize += Field.MinimumSize
1336
+ end
1337
+
1338
+ return Define(function()
1339
+ const Values = {}
1340
+
1341
+ for Index, Reader in Readers do
1342
+ Values[Keys[Index]] = Reader()
1343
+ end
1344
+
1345
+ return Values
1346
+ end, function(Values: { [string]: any })
1347
+ for Index, Writer in Writers do
1348
+ Writer(Values[Keys[Index]])
1349
+ end
1350
+ end, MinimumSize)
1351
+ end
1352
+
1353
+ function Schema.Optional(Inner: any): Schema
1354
+ const InnerSchema = Compile(Inner)
1355
+ const ReadInner, WriteInner = InnerSchema.Read, InnerSchema.Write
1356
+
1357
+ return Define(function()
1358
+ if ReadU8() == 0 then
1359
+ return nil
1360
+ end
1361
+
1362
+ return ReadInner()
1363
+ end, function(Value: any)
1364
+ Allocate(1)
1365
+
1366
+ if Value == nil then
1367
+ WriteU8(0)
1368
+ else
1369
+ WriteU8(1)
1370
+ WriteInner(Value)
1371
+ end
1372
+ end, 1)
1373
+ end
1374
+
1375
+ function Schema.Static(Values: { any }): Schema
1376
+ const Count = #Values
1377
+
1378
+ if Count < 1 or Count > 65536 then
1379
+ error("[swiftpacket] a static list needs from 1 to 65536 values", 2)
1380
+ end
1381
+
1382
+ const Indices: { [any]: number } = {}
1383
+
1384
+ for Index, Value in Values do
1385
+ Indices[Value] = Index - 1
1386
+ end
1387
+
1388
+ const Wide = Count > 256
1389
+
1390
+ return Define(function()
1391
+ const Value = Values[(if Wide then ReadU16() else ReadU8()) + 1]
1392
+
1393
+ if Value == nil then
1394
+ error("[swiftpacket] rejected an index outside the static list", 0)
1395
+ end
1396
+
1397
+ return Value
1398
+ end, function(Value: any)
1399
+ const Index = Indices[Value]
1400
+
1401
+ if Index == nil then
1402
+ WriteFailure(`{tostring(Value)} is not in the static list`)
1403
+ end
1404
+
1405
+ if Wide then
1406
+ Allocate(2)
1407
+ WriteU16(Index)
1408
+ else
1409
+ Allocate(1)
1410
+ WriteU8(Index)
1411
+ end
1412
+ end, if Wide then 2 else 1)
1413
+ end
1414
+
1415
+ function Schema.Flags(Names: { string }): Schema
1416
+ const Count = #Names
1417
+
1418
+ if Count < 1 or Count > 64 then
1419
+ error("[swiftpacket] flags need from 1 to 64 names", 2)
1420
+ end
1421
+
1422
+ const Bytes = math.ceil(Count / 8)
1423
+
1424
+ return Define(function()
1425
+ const Values: { [string]: boolean } = {}
1426
+
1427
+ for Index, Name in Names do
1428
+ Values[Name] = ReadBits(Index - 1, 1) == 1
1429
+ end
1430
+
1431
+ Skip(Bytes)
1432
+
1433
+ return Values
1434
+ end, function(Values: { [string]: boolean })
1435
+ Allocate(Bytes)
1436
+
1437
+ for Index, Name in Names do
1438
+ WriteBits(Index - 1, 1, if Values[Name] then 1 else 0)
1439
+ end
1440
+
1441
+ Skip(Bytes)
1442
+ end, Bytes)
1443
+ end
1444
+
1445
+ function Schema.Range(Inner: any, Minimum: number, Maximum: number): Schema
1446
+ const InnerSchema = Compile(Inner)
1447
+ const ReadInner, WriteInner = InnerSchema.Read, InnerSchema.Write
1448
+
1449
+ if Minimum > Maximum then
1450
+ error("[swiftpacket] range minimum is larger than its maximum", 2)
1451
+ end
1452
+
1453
+ return Define(function()
1454
+ const Value = ReadInner()
1455
+
1456
+ if type(Value) ~= "number" or Value < Minimum or Value > Maximum then
1457
+ error(`[swiftpacket] rejected {tostring(Value)} outside the range {Minimum} to {Maximum}`, 0)
1458
+ end
1459
+
1460
+ return Value
1461
+ end, function(Value: number)
1462
+ if Value < Minimum or Value > Maximum then
1463
+ WriteFailure(`{Value} is outside the range {Minimum} to {Maximum}`)
1464
+ end
1465
+
1466
+ WriteInner(Value)
1467
+ end, InnerSchema.MinimumSize)
1468
+ end
1469
+
1470
+ function Schema.InstanceOf(ClassName: string): Schema
1471
+ return Define(function()
1472
+ const Value = ReadInstance()
1473
+
1474
+ if Value and Value:IsA(ClassName) then
1475
+ return Value
1476
+ end
1477
+
1478
+ return nil
1479
+ end, function(Value: Instance?)
1480
+ if Value and not Value:IsA(ClassName) then
1481
+ WriteFailure(`{Value:GetFullName()} is not a {ClassName}`)
1482
+ end
1483
+
1484
+ WriteInstance(Value)
1485
+ end, 0)
1486
+ end
1487
+
1488
+ function Schema.EnumItem(EnumType: Enum): Schema
1489
+ return Define(function()
1490
+ const Item = EnumType:FromValue(ReadU16())
1491
+
1492
+ if Item == nil then
1493
+ error(`[swiftpacket] rejected an unknown {tostring(EnumType)} value`, 0)
1494
+ end
1495
+
1496
+ return Item
1497
+ end, function(Value: EnumItem)
1498
+ if Value.EnumType ~= EnumType then
1499
+ WriteFailure(`{tostring(Value)} is not a {tostring(EnumType)}`)
1500
+ end
1501
+
1502
+ Allocate(2)
1503
+ WriteU16(Value.Value)
1504
+ end, 2)
1505
+ end
1506
+
1507
+ function Schema.CompileList(Parameters: Arguments): { Schema }
1508
+ const Compiled: { Schema } = {}
1509
+
1510
+ for Index = 1, Parameters.n do
1511
+ Compiled[Index] = Compile(Parameters[Index])
1512
+ end
1513
+
1514
+ return Compiled
1515
+ end
1516
+
1517
+ const function WriteValues(Schemas: { Schema }, ...: any)
1518
+ const Count = #Schemas
1519
+
1520
+ if Count == 1 then
1521
+ Schemas[1].Write((...))
1522
+ elseif Count == 2 then
1523
+ const First, Second = ...
1524
+
1525
+ Schemas[1].Write(First)
1526
+ Schemas[2].Write(Second)
1527
+ elseif Count > 2 then
1528
+ const Values = { ... }
1529
+
1530
+ for Index = 1, Count do
1531
+ Schemas[Index].Write(Values[Index])
1532
+ end
1533
+ end
1534
+ end
1535
+
1536
+ function Schema.WriteEvent(Target: Cursor, Id: number, Name: string, Schemas: { Schema }, ...: any)
1537
+ Writing = Name
1538
+ Import(Target)
1539
+ WriteId(Id)
1540
+ WriteValues(Schemas, ...)
1541
+ Export()
1542
+ end
1543
+
1544
+ function Schema.WriteRequest(Target: Cursor, Id: number, CallIndex: number, Name: string, Schemas: { Schema }, ...: any)
1545
+ Writing = Name
1546
+ Import(Target)
1547
+ WriteId(Id)
1548
+ Allocate(2)
1549
+ WriteU16(CallIndex)
1550
+ WriteValues(Schemas, ...)
1551
+ Export()
1552
+ end
1553
+
1554
+ function Schema.WriteBody(Target: Cursor, Name: string, Schemas: { Schema }, ...: any)
1555
+ Writing = Name
1556
+ Reset(Target)
1557
+ Import(Target)
1558
+ WriteValues(Schemas, ...)
1559
+ Export()
1560
+ end
1561
+
1562
+ function Schema.WriteArguments(Schemas: { Schema }, Values: Arguments, Offset: number, Name: string)
1563
+ Writing = Name
1564
+
1565
+ for Index, Definition in Schemas do
1566
+ Definition.Write(Values[Index + Offset])
1567
+ end
1568
+ end
1569
+
1570
+ const SerializeScratch = NewCursor()
1571
+
1572
+ function Schema.Serialize(Name: string, Schemas: { Schema }, ...: any): (buffer, { any }?)
1573
+ Schema.WriteBody(SerializeScratch, Name, Schemas, ...)
1574
+
1575
+ const Data, Attached = Take(SerializeScratch)
1576
+ Reset(SerializeScratch)
1577
+
1578
+ return Data, Attached
1579
+ end
1580
+
1581
+ function Schema.Deserialize(Schemas: { Schema }, Source: buffer, SourceInstances: { any }): ...any
1582
+ Load(Source, SourceInstances, false, 0, 0)
1583
+
1584
+ const Count = #Schemas
1585
+
1586
+ if Count == 1 then
1587
+ return Schemas[1].Read()
1588
+ elseif Count == 2 then
1589
+ const First = Schemas[1].Read()
1590
+ const Second = Schemas[2].Read()
1591
+
1592
+ return First, Second
1593
+ end
1594
+
1595
+ const Values = table.create(Count)
1596
+
1597
+ for Index = 1, Count do
1598
+ Values[Index] = Schemas[Index].Read()
1599
+ end
1600
+
1601
+ return table.unpack(Values, 1, Count)
1602
+ end
1603
+
1604
+ Schema.Cursor = CursorLibrary
1605
+
1606
+ return Schema