@axpecter/lync 2.3.1 → 2.3.2

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.
@@ -4,6 +4,7 @@
4
4
 
5
5
  local Channel = require(script.Parent.Parent.Parent.internal.Channel)
6
6
  local Log = require(script.Parent.Parent.Parent.util.Log)
7
+ local Shared = require(script.Parent.Parent.composite.Shared)
7
8
  local Types = require(script.Parent.Parent.Parent.Types)
8
9
 
9
10
  -- Private ----------------------------------------------------------------
@@ -37,6 +38,7 @@ function Tagged.define(
37
38
  local nameToTag: { [string]: number } = {}
38
39
  local tagToCodec = table.create(variantCount)
39
40
  local hasUnknown = false
41
+ local hasDelta = false
40
42
 
41
43
  for i = 1, variantCount do
42
44
  local name = names[i]
@@ -46,6 +48,9 @@ function Tagged.define(
46
48
  if codec._hasUnknown then
47
49
  hasUnknown = true
48
50
  end
51
+ if Shared.containsDelta(codec) then
52
+ hasDelta = true
53
+ end
49
54
  end
50
55
 
51
56
  table.freeze(names)
@@ -89,6 +94,9 @@ function Tagged.define(
89
94
  if hasUnknown then
90
95
  codec._hasUnknown = true
91
96
  end
97
+ if hasDelta then
98
+ codec._hasDelta = true
99
+ end
92
100
  return table.freeze(codec)
93
101
  end
94
102
 
@@ -4,6 +4,7 @@
4
4
 
5
5
  local Base = require(script.Parent.Parent.Base)
6
6
  local Log = require(script.Parent.Parent.Parent.util.Log)
7
+ local Shared = require(script.Parent.Parent.composite.Shared)
7
8
  local Types = require(script.Parent.Parent.Parent.Types)
8
9
 
9
10
  -- Private ----------------------------------------------------------------
@@ -19,13 +20,17 @@ function Tuple.define(...: Types.InternalCodec<any>): Types.InternalCodec<any>
19
20
  local count = #codecs
20
21
  Log.assert(count > 0, "requires at least one codec")
21
22
 
22
- -- Single pass: detect all-direct fast path + propagate _hasUnknown.
23
+ -- Single pass: detect all-direct fast path + propagate _hasUnknown / _hasDelta.
23
24
  local allDirect, totalSize, hasUnknown = true, 0, false
25
+ local hasDelta = false
24
26
  for i = 1, count do
25
27
  local c = codecs[i]
26
28
  if c._hasUnknown then
27
29
  hasUnknown = true
28
30
  end
31
+ if Shared.containsDelta(c) then
32
+ hasDelta = true
33
+ end
29
34
  if allDirect then
30
35
  if c._size and c._directWrite and c._directRead then
31
36
  totalSize += c._size
@@ -96,6 +101,9 @@ function Tuple.define(...: Types.InternalCodec<any>): Types.InternalCodec<any>
96
101
  if hasUnknown then
97
102
  codec._hasUnknown = true
98
103
  end
104
+ if hasDelta then
105
+ codec._hasDelta = true
106
+ end
99
107
  return table.freeze(codec)
100
108
  end
101
109
 
@@ -126,6 +134,9 @@ function Tuple.define(...: Types.InternalCodec<any>): Types.InternalCodec<any>
126
134
  if hasUnknown then
127
135
  codec._hasUnknown = true
128
136
  end
137
+ if hasDelta then
138
+ codec._hasDelta = true
139
+ end
129
140
  return table.freeze(codec)
130
141
  end
131
142
 
@@ -23,9 +23,7 @@ local function writeBuffer(ch: Types.ChannelState, value: buffer): ()
23
23
  end
24
24
  local b = ch.buff
25
25
  buffer.writeu8(b, cursor, len)
26
- if len > 0 then
27
- buffer.copy(b, cursor + 1, value, 0, len)
28
- end
26
+ buffer.copy(b, cursor + 1, value, 0, len)
29
27
  ch.cursor = cursor + 1 + len
30
28
  return
31
29
  end
@@ -3,6 +3,7 @@
3
3
  -- CFrame codec. Default lossless (24B); __call yields smallest-three quat (16B).
4
4
 
5
5
  local Base = require(script.Parent.Parent.Base)
6
+ local Quat = require(script.Parent.Parent.Parent.util.Quat)
6
7
  local Types = require(script.Parent.Parent.Parent.Types)
7
8
 
8
9
  -- Constants --------------------------------------------------------------
@@ -10,10 +11,6 @@ local Types = require(script.Parent.Parent.Parent.Types)
10
11
  local EPSILON = 2.4e-7
11
12
  local EPSILON_SQ = EPSILON * EPSILON
12
13
 
13
- local INV_SQRT2 = 1 / math.sqrt(2)
14
- local Q_SCALE = 1023 / (2 * INV_SQRT2)
15
- local Q_INV = (2 * INV_SQRT2) / 1023
16
-
17
14
  -- Private ----------------------------------------------------------------
18
15
 
19
16
  local alloc = Base.alloc
@@ -21,19 +18,10 @@ local writef32 = buffer.writef32
21
18
  local readf32 = buffer.readf32
22
19
  local writeu32 = buffer.writeu32
23
20
  local readu32 = buffer.readu32
24
-
25
21
  local sqrt = math.sqrt
26
- local cos = math.cos
27
- local sin = math.sin
28
- local abs = math.abs
29
- local round = math.round
30
- local acos = math.acos
31
- local clamp = math.clamp
32
-
33
- local band = bit32.band
34
- local bor = bit32.bor
35
- local lshift = bit32.lshift
36
- local rshift = bit32.rshift
22
+ local quatPack = Quat.pack
23
+ local quatUnpack = Quat.unpack
24
+ local quatFromCFrame = Quat.fromCFrame
37
25
 
38
26
  local function writeLossless(b: buffer, off: number, value: CFrame): ()
39
27
  local pos = value.Position
@@ -82,102 +70,14 @@ local function writeCompressed(b: buffer, off: number, value: CFrame): ()
82
70
  writef32(b, off, pos.X)
83
71
  writef32(b, off + 4, pos.Y)
84
72
  writef32(b, off + 8, pos.Z)
85
-
86
- local axis, angle = value:ToAxisAngle()
87
- local h = angle * 0.5
88
- local s = sin(h)
89
- local qx = axis.X * s
90
- local qy = axis.Y * s
91
- local qz = axis.Z * s
92
- local qw = cos(h)
93
-
94
- -- Smallest-three: drop the largest |component|; track index alongside maxVal.
95
- local ax, ay, az, aw = abs(qx), abs(qy), abs(qz), abs(qw)
96
- local largest, maxVal = 3, aw
97
- if ax > maxVal then
98
- largest, maxVal = 0, ax
99
- end
100
- if ay > maxVal then
101
- largest, maxVal = 1, ay
102
- end
103
- if az > maxVal then
104
- largest, maxVal = 2, az
105
- end
106
-
107
- --[[
108
- Force the dropped component non-negative so the decoder's
109
- +sqrt(1 - a^2 - b^2 - c^2) reconstructs the right sign. Dispatch on
110
- `largest` instead of building a 4-element table per call.
111
- ]]
112
- local a: number, bComp: number, c: number
113
- if largest == 0 then
114
- if qx < 0 then
115
- qy, qz, qw = -qy, -qz, -qw
116
- end
117
- a, bComp, c = qy, qz, qw
118
- elseif largest == 1 then
119
- if qy < 0 then
120
- qx, qz, qw = -qx, -qz, -qw
121
- end
122
- a, bComp, c = qx, qz, qw
123
- elseif largest == 2 then
124
- if qz < 0 then
125
- qx, qy, qw = -qx, -qy, -qw
126
- end
127
- a, bComp, c = qx, qy, qw
128
- else
129
- if qw < 0 then
130
- qx, qy, qz = -qx, -qy, -qz
131
- end
132
- a, bComp, c = qx, qy, qz
133
- end
134
-
135
- local qa = clamp(round((a + INV_SQRT2) * Q_SCALE), 0, 1023)
136
- local qb = clamp(round((bComp + INV_SQRT2) * Q_SCALE), 0, 1023)
137
- local qc = clamp(round((c + INV_SQRT2) * Q_SCALE), 0, 1023)
138
-
139
- writeu32(b, off + 12, bor(lshift(largest, 30), lshift(qa, 20), lshift(qb, 10), qc))
73
+ writeu32(b, off + 12, quatPack(quatFromCFrame(value)))
140
74
  end
141
75
 
142
76
  local function readCompressed(b: buffer, off: number): CFrame
143
77
  local px = readf32(b, off)
144
78
  local py = readf32(b, off + 4)
145
79
  local pz = readf32(b, off + 8)
146
-
147
- local packed = readu32(b, off + 12)
148
- local largest = rshift(packed, 30)
149
- local qa = band(rshift(packed, 20), 0x3FF)
150
- local qb = band(rshift(packed, 10), 0x3FF)
151
- local qc = band(packed, 0x3FF)
152
-
153
- local a = qa * Q_INV - INV_SQRT2
154
- local bComp = qb * Q_INV - INV_SQRT2
155
- local c = qc * Q_INV - INV_SQRT2
156
-
157
- local sqSum = a * a + bComp * bComp + c * c
158
- local d = sqrt(if sqSum < 1 then 1 - sqSum else 0)
159
-
160
- -- Mirror encoder: dropped index gets `d`, others receive (a, bComp, c).
161
- local qx: number, qy: number, qz: number, qw: number
162
- if largest == 0 then
163
- qx, qy, qz, qw = d, a, bComp, c
164
- elseif largest == 1 then
165
- qx, qy, qz, qw = a, d, bComp, c
166
- elseif largest == 2 then
167
- qx, qy, qz, qw = a, bComp, d, c
168
- else
169
- qx, qy, qz, qw = a, bComp, c, d
170
- end
171
-
172
- local origin = CFrame.new(px, py, pz)
173
- local sqLen = qx * qx + qy * qy + qz * qz
174
- if sqLen < EPSILON_SQ then
175
- return origin
176
- end
177
-
178
- local angle = 2 * acos(clamp(qw, -1, 1))
179
- local inv = 1 / sqrt(sqLen)
180
- return origin * CFrame.fromAxisAngle(Vector3.new(qx * inv, qy * inv, qz * inv), angle)
80
+ return CFrame.new(px, py, pz) * quatUnpack(readu32(b, off + 12))
181
81
  end
182
82
 
183
83
  local function compressedFactory(_self): Types.InternalCodec<CFrame>
@@ -0,0 +1,390 @@
1
+ --!strict
2
+ --!native
3
+ -- Delta scalars: zigzag-varint diff against previous frame for ints, floats, vec3, CFrame.
4
+
5
+ local Base = require(script.Parent.Parent.Base)
6
+ local Baseline = require(script.Parent.Parent.Parent.internal.Baseline)
7
+ local Log = require(script.Parent.Parent.Parent.util.Log)
8
+ local Quantize = require(script.Parent.Parent.Parent.util.Quantize)
9
+ local Quat = require(script.Parent.Parent.Parent.util.Quat)
10
+ local Shared = require(script.Parent.Parent.composite.Shared)
11
+ local Types = require(script.Parent.Parent.Parent.Types)
12
+ local Varint = require(script.Parent.Parent.primitive.Varint)
13
+ local Zint = require(script.Parent.Parent.primitive.Zint)
14
+
15
+ -- Constants --------------------------------------------------------------
16
+
17
+ local I32_MIN = -0x80000000
18
+ local I32_MAX = 0x7FFFFFFF
19
+
20
+ -- deltaCFrame header bits: which components changed since the last frame.
21
+ local CF_X = 0x01
22
+ local CF_Y = 0x02
23
+ local CF_Z = 0x04
24
+ local CF_ROT = 0x08
25
+ local CF_ALL = 0x0F
26
+
27
+ -- Private ----------------------------------------------------------------
28
+
29
+ local ensure = Base.ensure
30
+ local allocDeltaId = Shared.allocDeltaId
31
+ local floor = math.floor
32
+ local clamp = math.clamp
33
+ local round = math.round
34
+ local band = bit32.band
35
+ local bor = bit32.bor
36
+ local readu8 = buffer.readu8
37
+ local writeu8 = buffer.writeu8
38
+ local readu32 = buffer.readu32
39
+ local writeu32 = buffer.writeu32
40
+ local bufLen = buffer.len
41
+ local varintWrite = Varint.write
42
+ local varintRead = Varint.read
43
+ local zigzagEncode = Zint.encode
44
+ local zigzagDecode = Zint.decode
45
+ local quatPack = Quat.pack
46
+ local quatUnpack = Quat.unpack
47
+ local quatFromCFrame = Quat.fromCFrame
48
+
49
+ local function quantizeVec3(
50
+ v: Vector3,
51
+ min: number,
52
+ delta: number,
53
+ scale: number
54
+ ): (number, number, number)
55
+ return round(clamp(v.X - min, 0, delta) * scale),
56
+ round(clamp(v.Y - min, 0, delta) * scale),
57
+ round(clamp(v.Z - min, 0, delta) * scale)
58
+ end
59
+
60
+ -- Reject out-of-range components rather than silently snapping via clamp().
61
+ local function validateVec3(v: Vector3, min: number, max: number, label: string): ()
62
+ if v.X < min or v.X > max or v.Y < min or v.Y > max or v.Z < min or v.Z > max then
63
+ Log.error(`{label} ({v.X}, {v.Y}, {v.Z}) has component out of range [{min}, {max}]`)
64
+ end
65
+ end
66
+
67
+ local function getScalarPrev(ch: Types.ChannelState, id: number, fallback: number): number
68
+ local cache = ch.deltas[id] :: any
69
+ return if cache then cache.scalar :: number else fallback
70
+ end
71
+
72
+ local function setScalarCache(ch: Types.ChannelState, id: number, value: number): ()
73
+ local cache = ch.deltas[id] :: any
74
+ if cache then
75
+ cache.scalar = value
76
+ else
77
+ ch.deltas[id] = { scalar = value } :: any
78
+ end
79
+ end
80
+
81
+ -- Public -----------------------------------------------------------------
82
+
83
+ local DeltaScalar = {}
84
+
85
+ --[[
86
+ Variable-length integer that emits zigzag varint of (current - previous).
87
+ First frame uses `min` as the implicit prev so the initial value encodes
88
+ as (value - min). Reliable transport only — a dropped frame desyncs prev.
89
+
90
+ Wire: 1 byte for unchanged or [-96, 95] mutations; up to 5 bytes for full i32.
91
+ ]]
92
+ function DeltaScalar.deltaInt(min: number, max: number): Types.InternalCodec<number>
93
+ if min > max then
94
+ Log.error(`min ({min}) must be <= max ({max})`)
95
+ end
96
+ if floor(min) ~= min or floor(max) ~= max then
97
+ Log.error("bounds must be integers")
98
+ end
99
+ if max - min > I32_MAX then
100
+ Log.error(`range too wide; (max - min) must fit in i32, got {max - min}`)
101
+ end
102
+
103
+ local deltaId = allocDeltaId()
104
+
105
+ return table.freeze({
106
+ _isDelta = true,
107
+ _typeCheck = "number",
108
+ _isInteger = true,
109
+ _min = min,
110
+ _max = max,
111
+
112
+ write = function(ch: Types.ChannelState, value: number): ()
113
+ if value < min or value > max then
114
+ Log.error(`value {value} out of range [{min}, {max}]`)
115
+ end
116
+ if floor(value) ~= value then
117
+ Log.error(`value {value} must be an integer`)
118
+ end
119
+ varintWrite(ch, zigzagEncode(value - getScalarPrev(ch, deltaId, min)))
120
+ setScalarCache(ch, deltaId, value)
121
+ end,
122
+
123
+ read = function(src: buffer, pos: number, _refs: { Instance }?): (number, number)
124
+ local raw, consumed = varintRead(src, pos)
125
+ if consumed == 0 then
126
+ Log.error("truncated deltaInt")
127
+ end
128
+ local cached = Baseline.getCache(deltaId)
129
+ local value = (if cached ~= nil then cached :: number else min) + zigzagDecode(raw)
130
+ if value < min or value > max then
131
+ Log.error(`deltaInt decoded value {value} out of range [{min}, {max}]`)
132
+ end
133
+ Baseline.setCache(deltaId, value)
134
+ return value, consumed
135
+ end,
136
+ }) :: Types.InternalCodec<number>
137
+ end
138
+
139
+ --[[
140
+ Quantized float with diffs in integer wire space (post-quantize). Storing
141
+ the quantized representative — not the float — across frames means
142
+ accumulating diffs over thousands of frames cannot drift past the grid.
143
+ ]]
144
+ function DeltaScalar.deltaFloat(
145
+ min: number,
146
+ max: number,
147
+ precision: number
148
+ ): Types.InternalCodec<number>
149
+ if max - min == 0 then
150
+ return Base.constant(min)
151
+ end
152
+
153
+ local _, _, _, scale, invScale, delta = Quantize.setup("Lync.deltaFloat", min, max, precision)
154
+ local deltaId = allocDeltaId()
155
+
156
+ return table.freeze({
157
+ _isDelta = true,
158
+ _typeCheck = "number",
159
+ _min = min,
160
+ _max = max,
161
+
162
+ write = function(ch: Types.ChannelState, value: number): ()
163
+ local q = round(clamp(value - min, 0, delta) * scale)
164
+ varintWrite(ch, zigzagEncode(q - getScalarPrev(ch, deltaId, 0)))
165
+ setScalarCache(ch, deltaId, q)
166
+ end,
167
+
168
+ read = function(src: buffer, pos: number, _refs: { Instance }?): (number, number)
169
+ local raw, consumed = varintRead(src, pos)
170
+ if consumed == 0 then
171
+ Log.error("truncated deltaFloat")
172
+ end
173
+ local cached = Baseline.getCache(deltaId)
174
+ local q = (if cached ~= nil then cached :: number else 0) + zigzagDecode(raw)
175
+ Baseline.setCache(deltaId, q)
176
+ return q * invScale + min, consumed
177
+ end,
178
+ }) :: Types.InternalCodec<number>
179
+ end
180
+
181
+ --[[
182
+ Quantized Vector3 with per-axis zigzag varint diffs. Static value: 3 bytes
183
+ (three zero zigzags); typical < 1 stud motion at 0.01-stud precision: 3-6
184
+ bytes. Baseline vec3 = 12 bytes, so 4x reduction at the static end.
185
+ ]]
186
+ function DeltaScalar.deltaVec3(
187
+ min: number,
188
+ max: number,
189
+ precision: number
190
+ ): Types.InternalCodec<Vector3>
191
+ if max - min == 0 then
192
+ return Base.constant(Vector3.new(min, min, min)) :: Types.InternalCodec<Vector3>
193
+ end
194
+
195
+ local _, _, _, scale, invScale, delta = Quantize.setup("Lync.deltaVec3", min, max, precision)
196
+ local deltaId = allocDeltaId()
197
+
198
+ return table.freeze({
199
+ _isDelta = true,
200
+ _typeCheck = "Vector3",
201
+ _min = min,
202
+ _max = max,
203
+
204
+ write = function(ch: Types.ChannelState, value: Vector3): ()
205
+ validateVec3(value, min, max, "Vector3")
206
+ local qx, qy, qz = quantizeVec3(value, min, delta, scale)
207
+ local cache = ch.deltas[deltaId] :: any
208
+ local px, py, pz = 0, 0, 0
209
+ if cache then
210
+ px, py, pz = cache.qx :: number, cache.qy :: number, cache.qz :: number
211
+ end
212
+
213
+ varintWrite(ch, zigzagEncode(qx - px))
214
+ varintWrite(ch, zigzagEncode(qy - py))
215
+ varintWrite(ch, zigzagEncode(qz - pz))
216
+
217
+ if cache then
218
+ cache.qx, cache.qy, cache.qz = qx, qy, qz
219
+ else
220
+ ch.deltas[deltaId] = { qx = qx, qy = qy, qz = qz } :: any
221
+ end
222
+ end,
223
+
224
+ read = function(src: buffer, pos: number, _refs: { Instance }?): (Vector3, number)
225
+ local rx, cx = varintRead(src, pos)
226
+ if cx == 0 then
227
+ Log.error("truncated deltaVec3 X")
228
+ end
229
+ local ry, cy = varintRead(src, pos + cx)
230
+ if cy == 0 then
231
+ Log.error("truncated deltaVec3 Y")
232
+ end
233
+ local rz, cz = varintRead(src, pos + cx + cy)
234
+ if cz == 0 then
235
+ Log.error("truncated deltaVec3 Z")
236
+ end
237
+
238
+ local cached = Baseline.getCache(deltaId) :: any
239
+ local px, py, pz = 0, 0, 0
240
+ if cached then
241
+ px, py, pz = cached.qx, cached.qy, cached.qz
242
+ end
243
+ local qx = px + zigzagDecode(rx)
244
+ local qy = py + zigzagDecode(ry)
245
+ local qz = pz + zigzagDecode(rz)
246
+ Baseline.setCache(deltaId, { qx = qx, qy = qy, qz = qz })
247
+
248
+ return Vector3.new(qx * invScale + min, qy * invScale + min, qz * invScale + min),
249
+ cx + cy + cz
250
+ end,
251
+ }) :: Types.InternalCodec<Vector3>
252
+ end
253
+
254
+ --[[
255
+ Quantized position + smallest-three quaternion rotation. Wire layout:
256
+ byte 0 header bits: bit0=Δx, bit1=Δy, bit2=Δz, bit3=Δrot
257
+ For each set position bit: zigzag varint of quantized diff
258
+ If rotation bit set: 4-byte packed quat
259
+
260
+ Static CFrame: 1 byte. Pos-only motion: 1 + 3 zint bytes (~4-7).
261
+ Full update: 1 + 3 zint + 4 quat (~8-13). Baseline lossless: 24 bytes.
262
+ ]]
263
+ function DeltaScalar.deltaCFrame(
264
+ posMin: number,
265
+ posMax: number,
266
+ posPrecision: number
267
+ ): Types.InternalCodec<CFrame>
268
+ -- Quantize.setup itself rejects min == max; the redundant guard is gone.
269
+ local _, _, _, scale, invScale, delta =
270
+ Quantize.setup("Lync.deltaCFrame", posMin, posMax, posPrecision)
271
+ local deltaId = allocDeltaId()
272
+
273
+ return table.freeze({
274
+ _isDelta = true,
275
+ _typeCheck = "CFrame",
276
+
277
+ write = function(ch: Types.ChannelState, value: CFrame): ()
278
+ local pos = value.Position
279
+ validateVec3(pos, posMin, posMax, "CFrame position")
280
+ local qx, qy, qz = quantizeVec3(pos, posMin, delta, scale)
281
+ local packed = quatPack(quatFromCFrame(value))
282
+
283
+ -- prev = 0 so the first frame writes absolute values; receiver also defaults to 0.
284
+ local cache = ch.deltas[deltaId] :: any
285
+ local flag: number
286
+ local px, py, pz = 0, 0, 0
287
+ if cache then
288
+ px = cache.qx :: number
289
+ py = cache.qy :: number
290
+ pz = cache.qz :: number
291
+ local ppacked = cache.qrot :: number
292
+ flag = 0
293
+ if qx ~= px then
294
+ flag = bor(flag, CF_X)
295
+ end
296
+ if qy ~= py then
297
+ flag = bor(flag, CF_Y)
298
+ end
299
+ if qz ~= pz then
300
+ flag = bor(flag, CF_Z)
301
+ end
302
+ if packed ~= ppacked then
303
+ flag = bor(flag, CF_ROT)
304
+ end
305
+ else
306
+ -- First frame: emit everything so the receiver has a baseline.
307
+ flag = CF_ALL
308
+ end
309
+
310
+ ensure(ch, 1)
311
+ writeu8(ch.buff, ch.cursor, flag)
312
+ ch.cursor += 1
313
+
314
+ if band(flag, CF_X) ~= 0 then
315
+ varintWrite(ch, zigzagEncode(qx - px))
316
+ end
317
+ if band(flag, CF_Y) ~= 0 then
318
+ varintWrite(ch, zigzagEncode(qy - py))
319
+ end
320
+ if band(flag, CF_Z) ~= 0 then
321
+ varintWrite(ch, zigzagEncode(qz - pz))
322
+ end
323
+ if band(flag, CF_ROT) ~= 0 then
324
+ ensure(ch, 4)
325
+ writeu32(ch.buff, ch.cursor, packed)
326
+ ch.cursor += 4
327
+ end
328
+
329
+ if cache then
330
+ cache.qx, cache.qy, cache.qz, cache.qrot = qx, qy, qz, packed
331
+ else
332
+ ch.deltas[deltaId] = { qx = qx, qy = qy, qz = qz, qrot = packed } :: any
333
+ end
334
+ end,
335
+
336
+ read = function(src: buffer, pos: number, _refs: { Instance }?): (CFrame, number)
337
+ if pos >= bufLen(src) then
338
+ Log.error("truncated deltaCFrame header")
339
+ end
340
+ local flag = readu8(src, pos)
341
+ local total = 1
342
+
343
+ local cached = Baseline.getCache(deltaId) :: any
344
+ local qx, qy, qz, packed = 0, 0, 0, 0
345
+ if cached then
346
+ qx, qy, qz, packed = cached.qx, cached.qy, cached.qz, cached.qrot
347
+ end
348
+
349
+ if band(flag, CF_X) ~= 0 then
350
+ local r, c = varintRead(src, pos + total)
351
+ if c == 0 then
352
+ Log.error("truncated deltaCFrame X")
353
+ end
354
+ qx += zigzagDecode(r)
355
+ total += c
356
+ end
357
+ if band(flag, CF_Y) ~= 0 then
358
+ local r, c = varintRead(src, pos + total)
359
+ if c == 0 then
360
+ Log.error("truncated deltaCFrame Y")
361
+ end
362
+ qy += zigzagDecode(r)
363
+ total += c
364
+ end
365
+ if band(flag, CF_Z) ~= 0 then
366
+ local r, c = varintRead(src, pos + total)
367
+ if c == 0 then
368
+ Log.error("truncated deltaCFrame Z")
369
+ end
370
+ qz += zigzagDecode(r)
371
+ total += c
372
+ end
373
+ if band(flag, CF_ROT) ~= 0 then
374
+ if pos + total + 4 > bufLen(src) then
375
+ Log.error("truncated deltaCFrame rotation")
376
+ end
377
+ packed = readu32(src, pos + total)
378
+ total += 4
379
+ end
380
+
381
+ Baseline.setCache(deltaId, { qx = qx, qy = qy, qz = qz, qrot = packed })
382
+
383
+ local position =
384
+ Vector3.new(qx * invScale + posMin, qy * invScale + posMin, qz * invScale + posMin)
385
+ return CFrame.new(position) * quatUnpack(packed), total
386
+ end,
387
+ }) :: Types.InternalCodec<CFrame>
388
+ end
389
+
390
+ return table.freeze(DeltaScalar)
@@ -35,7 +35,7 @@ local U32_MAX = Constants.U32_MAX
35
35
 
36
36
  -- Private ----------------------------------------------------------------
37
37
 
38
- local alloc = Base.alloc
38
+ local ensure = Base.ensure
39
39
  local band = bit32.band
40
40
  local bor = bit32.bor
41
41
  local rshift = bit32.rshift
@@ -46,18 +46,24 @@ local readu8 = buffer.readu8
46
46
  local readu16 = buffer.readu16
47
47
  local readu32 = buffer.readu32
48
48
 
49
- local function ensure(ch: Types.ChannelState, n: number): ()
50
- if ch.cursor + n > ch.size then
51
- alloc(ch, n)
52
- end
53
- end
54
-
55
49
  -- Public -----------------------------------------------------------------
56
50
 
57
51
  local Varint = {}
58
52
 
59
53
  Varint.INLINE_MAX = INLINE_MAX
60
54
 
55
+ -- Wire byte count for a value, without encoding it. Used by cost heuristics.
56
+ function Varint.length(value: number): number
57
+ if value <= INLINE_MAX then
58
+ return 1
59
+ elseif value <= TWO_MAX then
60
+ return 2
61
+ elseif value <= THREE_MAX then
62
+ return 3
63
+ end
64
+ return 5
65
+ end
66
+
61
67
  function Varint.write(ch: Types.ChannelState, value: number): ()
62
68
  if value < 0 or value > U32_MAX or value % 1 ~= 0 then
63
69
  Log.error(`value must be an integer in [0, {U32_MAX}], got {value}`)