@axpecter/lync 2.3.3 → 3.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.
- package/LICENSE +21 -0
- package/README.md +302 -299
- package/Types.luau +215 -0
- package/api/Define.luau +154 -0
- package/api/Lifecycle.luau +119 -0
- package/api/Packet.luau +108 -0
- package/api/Query.luau +149 -0
- package/api/Set.luau +189 -0
- package/channel/Blocks.luau +124 -0
- package/channel/Group.luau +118 -0
- package/channel/Link.luau +135 -0
- package/channel/Outbox.luau +261 -0
- package/channel/Recipients.luau +129 -0
- package/channel/Transport.luau +138 -0
- package/codec/Base.luau +236 -0
- package/codec/Composites.luau +128 -0
- package/codec/Datatypes.luau +162 -0
- package/codec/Legality.luau +61 -0
- package/codec/Scalars.luau +72 -0
- package/codec/Text.luau +89 -0
- package/codec/init.luau +39 -0
- package/index.d.ts +285 -0
- package/init.luau +91 -0
- package/kernel/Bits.luau +16 -0
- package/kernel/Catalog.luau +121 -0
- package/kernel/Heap.luau +80 -0
- package/kernel/Listeners.luau +95 -0
- package/kernel/Log.luau +197 -0
- package/kernel/Tally.luau +128 -0
- package/kernel/Threads.luau +44 -0
- package/kernel/Wide.luau +267 -0
- package/package.json +15 -32
- package/runtime/Boundary.luau +180 -0
- package/runtime/Dispatch.luau +91 -0
- package/runtime/Handles.luau +114 -0
- package/runtime/Handshake.luau +90 -0
- package/runtime/Pending.luau +196 -0
- package/runtime/Router.luau +379 -0
- package/runtime/Session.luau +731 -0
- package/shape/Compile.luau +340 -0
- package/shape/Folds.luau +332 -0
- package/shape/Frames.luau +170 -0
- package/shape/Grid.luau +169 -0
- package/shape/Ir.luau +540 -0
- package/shape/Joins.luau +125 -0
- package/shape/Legality.luau +273 -0
- package/shape/Lower.luau +419 -0
- package/shape/Order.luau +64 -0
- package/shape/Regions.luau +113 -0
- package/shape/Rotation.luau +472 -0
- package/shape/Serial.luau +258 -0
- package/shape/Words.luau +89 -0
- package/shape/emit/Block.luau +453 -0
- package/shape/emit/Column.luau +338 -0
- package/shape/emit/Leaf.luau +462 -0
- package/shape/emit/Word.luau +234 -0
- package/shape/emit/init.luau +222 -0
- package/shape/init.luau +78 -0
- package/state/Apply.luau +330 -0
- package/state/Audience.luau +126 -0
- package/state/Catchup.luau +218 -0
- package/state/Coalesce.luau +43 -0
- package/state/Fieldset.luau +177 -0
- package/state/Newest.luau +339 -0
- package/state/Plan.luau +441 -0
- package/state/Sections.luau +468 -0
- package/state/Store.luau +188 -0
- package/state/Sync.luau +762 -0
- package/wire/Bytes.luau +118 -0
- package/wire/Clock.luau +37 -0
- package/wire/Protocol.luau +110 -0
- package/wire/Sequence.luau +40 -0
- package/wire/Varint.luau +124 -0
- package/src/Types.luau +0 -180
- package/src/api/Group.luau +0 -156
- package/src/api/Packet.luau +0 -317
- package/src/api/Query.luau +0 -338
- package/src/api/Scope.luau +0 -90
- package/src/api/Signal.luau +0 -193
- package/src/codec/Base.luau +0 -136
- package/src/codec/composite/Array.luau +0 -473
- package/src/codec/composite/Map.luau +0 -485
- package/src/codec/composite/Optional.luau +0 -54
- package/src/codec/composite/Shared.luau +0 -311
- package/src/codec/composite/Struct.luau +0 -494
- package/src/codec/composite/Tagged.luau +0 -103
- package/src/codec/composite/Tuple.luau +0 -143
- package/src/codec/datatype/Buffer.luau +0 -66
- package/src/codec/datatype/CFrame.luau +0 -109
- package/src/codec/datatype/Color.luau +0 -32
- package/src/codec/datatype/Instance.luau +0 -52
- package/src/codec/datatype/IntVector.luau +0 -39
- package/src/codec/datatype/NumberRange.luau +0 -39
- package/src/codec/datatype/Ray.luau +0 -36
- package/src/codec/datatype/Rect.luau +0 -31
- package/src/codec/datatype/Region.luau +0 -62
- package/src/codec/datatype/Sequence.luau +0 -184
- package/src/codec/datatype/String.luau +0 -94
- package/src/codec/datatype/UDim.luau +0 -46
- package/src/codec/datatype/Vector.luau +0 -130
- package/src/codec/meta/Auto.luau +0 -318
- package/src/codec/meta/Bitfield.luau +0 -160
- package/src/codec/meta/Custom.luau +0 -33
- package/src/codec/meta/DeltaScalar.luau +0 -390
- package/src/codec/meta/Enum.luau +0 -50
- package/src/codec/meta/Float.luau +0 -38
- package/src/codec/meta/Nothing.luau +0 -13
- package/src/codec/meta/Unknown.luau +0 -41
- package/src/codec/primitive/Bool.luau +0 -26
- package/src/codec/primitive/Float16.luau +0 -99
- package/src/codec/primitive/Int.luau +0 -59
- package/src/codec/primitive/Number.luau +0 -24
- package/src/codec/primitive/Signed.luau +0 -32
- package/src/codec/primitive/Varint.luau +0 -165
- package/src/codec/primitive/Zint.luau +0 -99
- package/src/index.d.ts +0 -423
- package/src/init.luau +0 -362
- package/src/internal/Baseline.luau +0 -57
- package/src/internal/Channel.luau +0 -484
- package/src/internal/Middleware.luau +0 -100
- package/src/internal/Pool.luau +0 -66
- package/src/internal/Registry.luau +0 -119
- package/src/internal/Transport.luau +0 -28
- package/src/transport/Bridge.luau +0 -110
- package/src/transport/Client.luau +0 -97
- package/src/transport/Gate.luau +0 -488
- package/src/transport/Reader.luau +0 -331
- package/src/transport/Server.luau +0 -233
- package/src/util/Array.luau +0 -18
- package/src/util/Buffer.luau +0 -90
- package/src/util/Constants.luau +0 -30
- package/src/util/Log.luau +0 -68
- package/src/util/Player.luau +0 -14
- package/src/util/Quantize.luau +0 -84
- package/src/util/Quat.luau +0 -124
package/wire/Bytes.luau
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- How a buffer holds things: a field at a bit, the order two ranges compare in, and a seal.
|
|
4
|
+
|
|
5
|
+
local Protocol = require (script.Parent.Protocol)
|
|
6
|
+
local Wide = require (script.Parent.Parent.kernel.Wide)
|
|
7
|
+
|
|
8
|
+
local Bytes = {}
|
|
9
|
+
|
|
10
|
+
-- Constants -----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
-- Bytes in the wide word, whichever of its two carriers this build got.
|
|
13
|
+
const STRIDE = 8
|
|
14
|
+
|
|
15
|
+
const BYTE = Protocol.BYTE_BITS
|
|
16
|
+
|
|
17
|
+
-- Public --------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
-- A run of bits as the whole bytes that hold it, rounded up.
|
|
20
|
+
function Bytes.whole (bits: number): number return math.ceil (bits / BYTE) end
|
|
21
|
+
|
|
22
|
+
-- One value of `width` bits at a bit offset, and the offset past it.
|
|
23
|
+
function Bytes.put (b: buffer, at: number, width: number, value: number): number
|
|
24
|
+
local held = at
|
|
25
|
+
local left = width
|
|
26
|
+
local of = value
|
|
27
|
+
|
|
28
|
+
while left > 0 do
|
|
29
|
+
local byte = held // BYTE
|
|
30
|
+
local shift = held % BYTE
|
|
31
|
+
local room = BYTE - shift
|
|
32
|
+
local step = if left < room then left else room
|
|
33
|
+
|
|
34
|
+
local was = if shift == 0 then 0 else buffer.readu8 (b, byte)
|
|
35
|
+
local piece = bit32.lshift (bit32.extract (of, 0, step), shift)
|
|
36
|
+
buffer.writeu8 (b, byte, bit32.bor (was, piece))
|
|
37
|
+
|
|
38
|
+
of = bit32.rshift (of, step)
|
|
39
|
+
held += step
|
|
40
|
+
left -= step
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
return at + width
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
--[[
|
|
47
|
+
The same value back, and the offset past it. A width of nothing is a value of nothing, which is
|
|
48
|
+
what a ranged int over one possibility is, and it reads no byte at all.
|
|
49
|
+
]]
|
|
50
|
+
function Bytes.take (b: buffer, at: number, width: number): (number, number)
|
|
51
|
+
local of = 0
|
|
52
|
+
local held = at
|
|
53
|
+
local left = width
|
|
54
|
+
local got = 0
|
|
55
|
+
|
|
56
|
+
while left > 0 do
|
|
57
|
+
local byte = held // BYTE
|
|
58
|
+
local shift = held % BYTE
|
|
59
|
+
local room = BYTE - shift
|
|
60
|
+
local step = if left < room then left else room
|
|
61
|
+
|
|
62
|
+
local piece = bit32.extract (buffer.readu8 (b, byte), shift, step)
|
|
63
|
+
of = bit32.bor (of, bit32.lshift (piece, got))
|
|
64
|
+
|
|
65
|
+
got += step
|
|
66
|
+
held += step
|
|
67
|
+
left -= step
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
return of, at + width
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
-- The byte a bit run ends on, and whether what it left above itself is zero.
|
|
74
|
+
function Bytes.aligned (b: buffer, at: number): (number, boolean)
|
|
75
|
+
local over = at % BYTE
|
|
76
|
+
if over == 0 then return at // BYTE, true end
|
|
77
|
+
|
|
78
|
+
local pad = bit32.rshift (buffer.readu8 (b, at // BYTE), over)
|
|
79
|
+
return at // BYTE + 1, pad == 0
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
--[[
|
|
83
|
+
The swap makes the word compare a byte compare: a read takes the first byte as the least of the
|
|
84
|
+
word, and reversing it spells the magnitude the bytes read as. A word is taken only where a
|
|
85
|
+
whole one remains inside the shorter range, because the bytes past it are not ours. Never
|
|
86
|
+
through a float, where plus and minus zero are equal and one NaN payload is unequal.
|
|
87
|
+
]]
|
|
88
|
+
@native
|
|
89
|
+
function Bytes.rangeLess (b: buffer, atA: number, lenA: number, atB: number, lenB: number): boolean
|
|
90
|
+
local shared = math.min (lenA, lenB)
|
|
91
|
+
local words = shared - shared % STRIDE
|
|
92
|
+
local at = 0
|
|
93
|
+
|
|
94
|
+
while at < words do
|
|
95
|
+
local wordA = Wide.ordered (b, atA + at)
|
|
96
|
+
local wordB = Wide.ordered (b, atB + at)
|
|
97
|
+
if not Wide.same (wordA, wordB) then return Wide.below (wordA, wordB) end
|
|
98
|
+
at += STRIDE
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
while at < shared do
|
|
102
|
+
local byteA = buffer.readu8 (b, atA + at)
|
|
103
|
+
local byteB = buffer.readu8 (b, atB + at)
|
|
104
|
+
if byteA ~= byteB then return byteA < byteB end
|
|
105
|
+
at += 1
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
return lenA < lenB
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
-- A copy rather than the scratch, because the next block encodes into that same scratch.
|
|
112
|
+
function Bytes.seal (scratch: buffer, length: number): buffer
|
|
113
|
+
local sealed = buffer.create (length)
|
|
114
|
+
buffer.copy (sealed, 0, scratch, 0, length)
|
|
115
|
+
return sealed
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
return table.freeze (Bytes)
|
package/wire/Clock.luau
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- Milliseconds against an epoch, the field they wrap into, and the instant one is unwrapped to.
|
|
4
|
+
|
|
5
|
+
local Protocol = require (script.Parent.Protocol)
|
|
6
|
+
|
|
7
|
+
local Clock = {}
|
|
8
|
+
|
|
9
|
+
-- Constants -----------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const RATE = Protocol.STAMP_RATE
|
|
12
|
+
const SPAN = 2 ^ Protocol.STAMP_BITS
|
|
13
|
+
|
|
14
|
+
-- Public --------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
--[[
|
|
17
|
+
Floored rather than rounded, so a count never runs ahead of the clock it read, and negative
|
|
18
|
+
where the reading is behind the epoch: a client's shared clock sits there for a moment after
|
|
19
|
+
start and clamping would move its stamps.
|
|
20
|
+
]]
|
|
21
|
+
function Clock.elapsed (now: number, epoch: number): number
|
|
22
|
+
local since = now - epoch
|
|
23
|
+
return math.floor (since * RATE)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
-- The remainder follows the divisor, so a negative count lands inside the field, not below it.
|
|
27
|
+
function Clock.stamp (elapsed: number): number return elapsed % SPAN end
|
|
28
|
+
|
|
29
|
+
--[[
|
|
30
|
+
The instant carrying this stamp nearest the estimate. At exactly half a space the two candidates
|
|
31
|
+
tie and the rounding takes the one further from the stamp, which is arbitrary and pinned.
|
|
32
|
+
]]
|
|
33
|
+
function Clock.unwrap (stamp: number, elapsed: number): number
|
|
34
|
+
return stamp + math.round ((elapsed - stamp) / SPAN) * SPAN
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
return table.freeze (Clock)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- The one home of every wire constant. Nothing else in the repository may state one.
|
|
4
|
+
|
|
5
|
+
local Protocol = {}
|
|
6
|
+
|
|
7
|
+
-- What both machines have to agree on before either spends a byte on the other's schema.
|
|
8
|
+
Protocol.VERSION = 4
|
|
9
|
+
|
|
10
|
+
--[[
|
|
11
|
+
The handshake lane's whole grammar: what each of the two messages weighs, where a verdict keeps
|
|
12
|
+
its one field, what a verdict says first, and the whole of what one can say.
|
|
13
|
+
]]
|
|
14
|
+
Protocol.HELLO_BYTES = 8
|
|
15
|
+
Protocol.VERDICT_BYTES = 9
|
|
16
|
+
Protocol.VERDICT_FIELD = 1
|
|
17
|
+
Protocol.VERDICT_REJECT = 0
|
|
18
|
+
Protocol.VERDICT_ACCEPT = 1
|
|
19
|
+
|
|
20
|
+
-- How a bit is addressed, which is the whole of what a byte means to this format.
|
|
21
|
+
Protocol.BYTE_BITS = 8
|
|
22
|
+
|
|
23
|
+
--[[
|
|
24
|
+
The number domain. A signed varint folds its sign into the value it carries, so it reaches half
|
|
25
|
+
as far in each direction and its edge sits a bit below the one a double counts exactly to.
|
|
26
|
+
]]
|
|
27
|
+
Protocol.EXACT = 9007199254740992
|
|
28
|
+
Protocol.VLI_EDGE = 4503599627370495
|
|
29
|
+
Protocol.VARINT_GROUPS = 8
|
|
30
|
+
Protocol.BIT_CEILING = 24
|
|
31
|
+
|
|
32
|
+
--[[
|
|
33
|
+
A varint group: the bit that says another follows, the seven under it, and half of those.
|
|
34
|
+
|
|
35
|
+
The format's own and not the encoder's, since a reader who has the bytes and not the source
|
|
36
|
+
has to be able to take one apart, and half a group is where a signed value's sign folds.
|
|
37
|
+
]]
|
|
38
|
+
Protocol.VARINT_BITS = 7
|
|
39
|
+
Protocol.VARINT_MORE = 0x80
|
|
40
|
+
Protocol.VARINT_GROUP = 0x7F
|
|
41
|
+
Protocol.VARINT_HALF = 0x40
|
|
42
|
+
|
|
43
|
+
-- Schema legality.
|
|
44
|
+
Protocol.MAX_FIELDS = 64
|
|
45
|
+
|
|
46
|
+
--[[
|
|
47
|
+
The unreliable lane. The margin is not measured and carries no date, because nothing has
|
|
48
|
+
measured it: zero is the documented engine threshold taken at its word, and a margin above it
|
|
49
|
+
is a number somebody would have to put a real network behind before writing down.
|
|
50
|
+
]]
|
|
51
|
+
Protocol.UNRELIABLE_CEILING = 1000
|
|
52
|
+
Protocol.UNRELIABLE_MARGIN = 0
|
|
53
|
+
Protocol.UNRELIABLE_CAP = Protocol.UNRELIABLE_CEILING - Protocol.UNRELIABLE_MARGIN
|
|
54
|
+
Protocol.MAX_FIRE = 1000000
|
|
55
|
+
|
|
56
|
+
-- Budgets, in bytes, and the detector window in seconds.
|
|
57
|
+
Protocol.BUDGET_RATE = 32768
|
|
58
|
+
Protocol.BUDGET_FLOOR = 1024
|
|
59
|
+
Protocol.SECTION_BYTES = Protocol.BUDGET_FLOOR
|
|
60
|
+
Protocol.DETECTOR_WINDOW = 1
|
|
61
|
+
|
|
62
|
+
-- Queries, the timeout in seconds.
|
|
63
|
+
Protocol.QUERY_TIMEOUT = 10
|
|
64
|
+
Protocol.QUERY_CAP = 32768
|
|
65
|
+
|
|
66
|
+
-- Sequences and stamps.
|
|
67
|
+
Protocol.NEWEST_WINDOW = 32768
|
|
68
|
+
Protocol.NEWEST_GENS = 4
|
|
69
|
+
Protocol.STAMP_BITS = 32
|
|
70
|
+
Protocol.STAMP_RATE = 1000
|
|
71
|
+
|
|
72
|
+
-- A packed colour's channel widths, least significant first.
|
|
73
|
+
Protocol.RGB565_RED = 5
|
|
74
|
+
Protocol.RGB565_GREEN = 6
|
|
75
|
+
Protocol.RGB565_BLUE = 5
|
|
76
|
+
|
|
77
|
+
--[[
|
|
78
|
+
Which component a turn left behind, in the order they are counted. The tag a frame carries is
|
|
79
|
+
one of these four, which is also why it is two bits wide.
|
|
80
|
+
]]
|
|
81
|
+
Protocol.QUAT_X = 0
|
|
82
|
+
Protocol.QUAT_Y = 1
|
|
83
|
+
Protocol.QUAT_Z = 2
|
|
84
|
+
Protocol.QUAT_W = 3
|
|
85
|
+
|
|
86
|
+
--[[
|
|
87
|
+
How far one component of the octahedral fold runs, in degrees, which is what a precision stated
|
|
88
|
+
for one is read against.
|
|
89
|
+
]]
|
|
90
|
+
Protocol.QUARTER_TURN = 90
|
|
91
|
+
|
|
92
|
+
--[[
|
|
93
|
+
How far the three that were kept can reach, which is the root of a half. The tag names the one
|
|
94
|
+
left out and it is the largest, so a kept component is no larger than that one and the two of
|
|
95
|
+
them squared cannot pass one, which puts both at this exactly where they are equal.
|
|
96
|
+
]]
|
|
97
|
+
Protocol.QUAT_BOUND = 0.7071067811865476
|
|
98
|
+
-- The response header's outcome, which is one bit and decides whether a payload follows it.
|
|
99
|
+
|
|
100
|
+
Protocol.OUTCOME_BITS = 1
|
|
101
|
+
|
|
102
|
+
-- What a section header says it is, and how it names the fields it carries.
|
|
103
|
+
Protocol.SECTION_KEYFRAME = 0
|
|
104
|
+
Protocol.SECTION_DELTA = 1
|
|
105
|
+
Protocol.SECTION_REMOVAL = 2
|
|
106
|
+
Protocol.SECTION_CLEAR = 3
|
|
107
|
+
Protocol.FIELDSET_LIST = 0
|
|
108
|
+
Protocol.FIELDSET_MASK = 1
|
|
109
|
+
|
|
110
|
+
return table.freeze (Protocol)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- The u16 ring an unreliable stream is ordered by, and the rate gate over it.
|
|
4
|
+
|
|
5
|
+
local Protocol = require (script.Parent.Protocol)
|
|
6
|
+
|
|
7
|
+
local Sequence = {}
|
|
8
|
+
|
|
9
|
+
-- Constants -----------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const WINDOW = Protocol.NEWEST_WINDOW
|
|
12
|
+
|
|
13
|
+
-- The ring a sequence counts round, which is the window doubled.
|
|
14
|
+
const SPACE = WINDOW * 2
|
|
15
|
+
|
|
16
|
+
-- Public --------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
function Sequence.next (n: number): number return (n + 1) % SPACE end
|
|
19
|
+
|
|
20
|
+
--[[
|
|
21
|
+
Newer is a distance under the window, so exactly half the space apart ranks neither way and a
|
|
22
|
+
sequence is never newer than itself. The subtraction goes negative and the remainder follows the
|
|
23
|
+
divisor, which is what carries it round the ring.
|
|
24
|
+
]]
|
|
25
|
+
function Sequence.isNewer (a: number, b: number): boolean
|
|
26
|
+
local apart = (a - b) % SPACE
|
|
27
|
+
return apart > 0 and apart < WINDOW
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
--[[
|
|
31
|
+
The interval runs from the last transmission, never from a division of the clock: a gate reading
|
|
32
|
+
the clock in buckets delivers the same rate and lets two flushes a bucket apart both through.
|
|
33
|
+
Two in one instant are one transmission.
|
|
34
|
+
]]
|
|
35
|
+
function Sequence.opened (now: number, last: number?, hz: number?): boolean
|
|
36
|
+
if last == nil or hz == nil then return true end
|
|
37
|
+
return now - last >= 1 / hz
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
return table.freeze (Sequence)
|
package/wire/Varint.luau
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- The two variable-length forms, `vlq` and `vli`.
|
|
4
|
+
|
|
5
|
+
local Log = require (script.Parent.Parent.kernel.Log)
|
|
6
|
+
local Protocol = require (script.Parent.Protocol)
|
|
7
|
+
|
|
8
|
+
local Varint = {}
|
|
9
|
+
|
|
10
|
+
-- Constants -----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
-- The continuation bit, the seven bits under it, and half a group, which the format owns.
|
|
13
|
+
const MORE = Protocol.VARINT_MORE
|
|
14
|
+
const GROUP = Protocol.VARINT_GROUP
|
|
15
|
+
const HALF = Protocol.VARINT_HALF
|
|
16
|
+
|
|
17
|
+
const EXACT = Protocol.EXACT
|
|
18
|
+
const GROUPS = Protocol.VARINT_GROUPS
|
|
19
|
+
|
|
20
|
+
-- Private -------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
--[[
|
|
23
|
+
A value is carried as a head group and the rest, never as one number: the zigzag of the widest
|
|
24
|
+
signed value wants fifty-four bits and a double holds fifty-three, and neither part wants more
|
|
25
|
+
than fifty-three. That is why nothing here reaches the integer domain.
|
|
26
|
+
]]
|
|
27
|
+
@native
|
|
28
|
+
local function laid (out: buffer, at: number, head: number, tail: number): number
|
|
29
|
+
while tail > 0 do
|
|
30
|
+
buffer.writeu8 (out, at, head + MORE)
|
|
31
|
+
at += 1
|
|
32
|
+
head, tail = tail % MORE, tail // MORE
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
buffer.writeu8 (out, at, head)
|
|
36
|
+
return at + 1
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
@native
|
|
40
|
+
local function taken (b: buffer, at: number): (number, number, number)
|
|
41
|
+
local head = buffer.readu8 (b, at)
|
|
42
|
+
at += 1
|
|
43
|
+
if head < MORE then return head, 0, at end
|
|
44
|
+
head -= MORE
|
|
45
|
+
|
|
46
|
+
local tail, shift = 0, 1
|
|
47
|
+
for _ = 2, GROUPS do
|
|
48
|
+
local byte = buffer.readu8 (b, at)
|
|
49
|
+
at += 1
|
|
50
|
+
if byte < MORE then return head, tail + byte * shift, at end
|
|
51
|
+
|
|
52
|
+
tail += (byte - MORE) * shift
|
|
53
|
+
shift *= MORE
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
-- Raised bare: this answers three numbers, where a call answering `never` reads as a fall thro
|
|
57
|
+
error (`a varint ran past {GROUPS} groups`, 0)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
-- Public --------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
--[[
|
|
63
|
+
Answers the offset past what it wrote, and refuses a value the exact domain has no place for.
|
|
64
|
+
A varint carries a whole number and the domain is what a double holds exactly, so what is
|
|
65
|
+
turned away here is a caller's own value rather than anything a frame could have carried.
|
|
66
|
+
]]
|
|
67
|
+
@native
|
|
68
|
+
function Varint.write (out: buffer, at: number, value: number): number
|
|
69
|
+
if value < 0 or value >= EXACT then
|
|
70
|
+
Log.refuse (`a varint carries none to {EXACT - 1}, not {value}`)
|
|
71
|
+
end
|
|
72
|
+
return laid (out, at, value % MORE, value // MORE)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
--[[
|
|
76
|
+
Answers the value and the offset past it, and refuses bytes that spell a number past the exact
|
|
77
|
+
domain. Refused rather than answered, because a value a double cannot hold exactly is a value
|
|
78
|
+
the two machines would disagree about the moment either did arithmetic on it.
|
|
79
|
+
]]
|
|
80
|
+
@native
|
|
81
|
+
function Varint.read (b: buffer, at: number): (number, number)
|
|
82
|
+
local head, tail, after = taken (b, at)
|
|
83
|
+
|
|
84
|
+
local value = head + tail * MORE
|
|
85
|
+
if value >= EXACT then Log.refuse (`a varint read {value}, past the exact ceiling`) end
|
|
86
|
+
return value, after
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
--[[
|
|
90
|
+
Taken off the magnitude rather than the doubled value. A negative one sitting on a group
|
|
91
|
+
boundary has nothing left in its head, so the head carries the whole group and the rest gives
|
|
92
|
+
one up, which is the one place the two branches differ.
|
|
93
|
+
]]
|
|
94
|
+
@native
|
|
95
|
+
function Varint.writeSigned (out: buffer, at: number, value: number): number
|
|
96
|
+
if value <= -EXACT or value >= EXACT then
|
|
97
|
+
Log.refuse (`a signed varint carries {1 - EXACT} to {EXACT - 1}, not {value}`)
|
|
98
|
+
end
|
|
99
|
+
if value >= 0 then return laid (out, at, (value % HALF) * 2, value // HALF) end
|
|
100
|
+
|
|
101
|
+
local magnitude = -value
|
|
102
|
+
local head = magnitude % HALF
|
|
103
|
+
if head == 0 then return laid (out, at, GROUP, magnitude // HALF - 1) end
|
|
104
|
+
return laid (out, at, head * 2 - 1, magnitude // HALF)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
--[[
|
|
108
|
+
The same pair for the signed form, refused at both ends of the domain rather than at one. A
|
|
109
|
+
reader that checked only the top would take a magnitude one past the bottom and answer a number
|
|
110
|
+
no encoder could have written.
|
|
111
|
+
]]
|
|
112
|
+
@native
|
|
113
|
+
function Varint.readSigned (b: buffer, at: number): (number, number)
|
|
114
|
+
local head, tail, after = taken (b, at)
|
|
115
|
+
|
|
116
|
+
local rest = tail * HALF
|
|
117
|
+
local value = if head % 2 == 0 then head / 2 + rest else -((head + 1) / 2 + rest)
|
|
118
|
+
if value <= -EXACT or value >= EXACT then
|
|
119
|
+
Log.refuse (`a signed varint read {value}, past the exact ceiling`)
|
|
120
|
+
end
|
|
121
|
+
return value, after
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
return table.freeze (Varint)
|
package/src/Types.luau
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
--!strict
|
|
2
|
-
-- Shared type definitions for the Lync networking library.
|
|
3
|
-
|
|
4
|
-
export type ChannelState = {
|
|
5
|
-
buff: buffer,
|
|
6
|
-
cursor: number,
|
|
7
|
-
size: number,
|
|
8
|
-
refs: { Instance },
|
|
9
|
-
refCount: number,
|
|
10
|
-
lastId: number,
|
|
11
|
-
countPos: number,
|
|
12
|
-
itemCount: number,
|
|
13
|
-
singleMode: boolean,
|
|
14
|
-
singlePos: number,
|
|
15
|
-
--[[
|
|
16
|
-
Per-codec write-side delta state, keyed by deltaId. Shape is opaque
|
|
17
|
-
and codec-internal: deltaStruct stores `{raw, len, segOff, segLen}`,
|
|
18
|
-
deltaArray stores `{perIdx, length}`, deltaMap stores `{perKey}`,
|
|
19
|
-
delta scalars store quantized previous values.
|
|
20
|
-
]]
|
|
21
|
-
deltas: { [number]: any },
|
|
22
|
-
prevDump: buffer?,
|
|
23
|
-
prevDumpLen: number,
|
|
24
|
-
currentPacket: string?,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type Codec<T> = {
|
|
28
|
-
write: (ch: ChannelState, value: T) -> (),
|
|
29
|
-
read: (src: buffer, pos: number, refs: { Instance }?) -> (T, number),
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
--[[
|
|
33
|
-
Optional metadata used by Gate (validation), Reader (max-item bound),
|
|
34
|
-
Auto (type dispatch), and composite codecs (direct write/read fast path).
|
|
35
|
-
]]
|
|
36
|
-
export type InternalCodec<T> = {
|
|
37
|
-
write: (ch: ChannelState, value: T) -> (),
|
|
38
|
-
read: (src: buffer, pos: number, refs: { Instance }?) -> (T, number),
|
|
39
|
-
_size: number?,
|
|
40
|
-
_directWrite: ((b: buffer, offset: number, value: T) -> ())?,
|
|
41
|
-
_directRead: ((b: buffer, offset: number) -> T)?,
|
|
42
|
-
_isDelta: boolean?,
|
|
43
|
-
--[[
|
|
44
|
-
True if any nested codec is delta. Lets the broadcast hot path skip
|
|
45
|
-
encode-once when delta state would diverge per-receiver.
|
|
46
|
-
]]
|
|
47
|
-
_hasDelta: boolean?,
|
|
48
|
-
_isBool: boolean?,
|
|
49
|
-
_hasUnknown: boolean?,
|
|
50
|
-
_min: number?,
|
|
51
|
-
_max: number?,
|
|
52
|
-
_isInteger: boolean?,
|
|
53
|
-
_schema: { [string]: InternalCodec<T> }?,
|
|
54
|
-
_typeCheck: string?,
|
|
55
|
-
--[[
|
|
56
|
-
Composite metadata: lets Gate recurse element/variant validation
|
|
57
|
-
instead of falling through to the generic NaN/inf scan.
|
|
58
|
-
]]
|
|
59
|
-
_isOptional: boolean?,
|
|
60
|
-
_inner: InternalCodec<T>?,
|
|
61
|
-
_isTagged: boolean?,
|
|
62
|
-
_variants: { [string]: InternalCodec<T> }?,
|
|
63
|
-
_tagField: string?,
|
|
64
|
-
_isArray: boolean?,
|
|
65
|
-
_element: InternalCodec<T>?,
|
|
66
|
-
_maxCount: number?,
|
|
67
|
-
_isMap: boolean?,
|
|
68
|
-
_keyCodec: InternalCodec<T>?,
|
|
69
|
-
_valueCodec: InternalCodec<T>?,
|
|
70
|
-
_isTuple: boolean?,
|
|
71
|
-
_elements: { InternalCodec<T> }?,
|
|
72
|
-
_isEnum: boolean?,
|
|
73
|
-
_enumValues: { [string]: number }?,
|
|
74
|
-
_maxStringLength: number?,
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export type RateLimitState = {
|
|
78
|
-
tokens: number,
|
|
79
|
-
lastRefill: number,
|
|
80
|
-
lastAccepted: number,
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
--[[
|
|
84
|
-
Token-bucket (maxPerSecond + optional burst) or cooldown (cooldown
|
|
85
|
-
seconds between accepts). Mixing both forms is undefined behavior.
|
|
86
|
-
]]
|
|
87
|
-
export type RateLimitConfig = {
|
|
88
|
-
maxPerSecond: number?,
|
|
89
|
-
burst: number?,
|
|
90
|
-
cooldown: number?,
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
--[[
|
|
94
|
-
User-supplied per-packet validator. Returns (true, nil) on accept;
|
|
95
|
-
(false, reason) on reject — the reason is forwarded to onDrop hooks.
|
|
96
|
-
]]
|
|
97
|
-
export type ValidateFn = (data: any, player: Player) -> (boolean, string?)
|
|
98
|
-
|
|
99
|
-
export type PacketOptions = {
|
|
100
|
-
unreliable: boolean?,
|
|
101
|
-
rateLimit: RateLimitConfig?,
|
|
102
|
-
validate: ValidateFn?,
|
|
103
|
-
maxPayloadBytes: number?,
|
|
104
|
-
timestamp: ("frame" | "offset" | "full")?,
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export type QueryOptions = {
|
|
108
|
-
timeout: number?,
|
|
109
|
-
rateLimit: RateLimitConfig?,
|
|
110
|
-
validate: ValidateFn?,
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export type Registration = {
|
|
114
|
-
id: number,
|
|
115
|
-
name: string,
|
|
116
|
-
kind: number,
|
|
117
|
-
codec: InternalCodec<any>,
|
|
118
|
-
isUnreliable: boolean,
|
|
119
|
-
-- Response registration id; set on REQUEST regs only.
|
|
120
|
-
partner: number?,
|
|
121
|
-
signal: SignalLike,
|
|
122
|
-
rateLimit: RateLimitConfig?,
|
|
123
|
-
validate: ValidateFn?,
|
|
124
|
-
needsGate: boolean,
|
|
125
|
-
maxPayloadBytes: number?,
|
|
126
|
-
timestampMode: number,
|
|
127
|
-
headerSize: number,
|
|
128
|
-
_openFn: (ch: ChannelState, id: number) -> (),
|
|
129
|
-
bytesSent: number,
|
|
130
|
-
bytesReceived: number,
|
|
131
|
-
fires: number,
|
|
132
|
-
recvFires: number,
|
|
133
|
-
drops: number,
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export type SignalLike = {
|
|
137
|
-
fire: (self: SignalLike, ...any) -> (),
|
|
138
|
-
fireSync: (self: SignalLike, ...any) -> (),
|
|
139
|
-
connect: (self: SignalLike, callback: (...any) -> ()) -> Connection,
|
|
140
|
-
once: (self: SignalLike, callback: (...any) -> ()) -> Connection,
|
|
141
|
-
wait: (self: SignalLike) -> ...any,
|
|
142
|
-
count: (self: SignalLike) -> number,
|
|
143
|
-
hasListeners: (self: SignalLike) -> boolean,
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export type Connection = {
|
|
147
|
-
connected: boolean,
|
|
148
|
-
disconnect: (self: Connection) -> (),
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export type ConfigureOptions = {
|
|
152
|
-
channelMaxSize: number?,
|
|
153
|
-
validationDepth: number?,
|
|
154
|
-
poolSize: number?,
|
|
155
|
-
bandwidthLimit: { softLimit: number, maxStrikes: number }?,
|
|
156
|
-
globalRateLimit: { maxPerSecond: number }?,
|
|
157
|
-
stats: boolean?,
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export type PacketStats = {
|
|
161
|
-
bytesSent: number,
|
|
162
|
-
bytesReceived: number,
|
|
163
|
-
fires: number,
|
|
164
|
-
recvFires: number,
|
|
165
|
-
drops: number,
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export type PlayerStats = {
|
|
169
|
-
bytesSent: number,
|
|
170
|
-
bytesReceived: number,
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
--[[
|
|
174
|
-
Server keys baselines per Player; client uses `false` since booleans are
|
|
175
|
-
valid table keys but Players never collide with that.
|
|
176
|
-
]]
|
|
177
|
-
export type BaselineKey = Player | false
|
|
178
|
-
|
|
179
|
-
-- Type-only module: runtime table is empty so re-requires are cheap.
|
|
180
|
-
return table.freeze({})
|