azul-sync 1.3.1 → 1.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.
Files changed (34) hide show
  1. package/dist/push.d.ts +2 -0
  2. package/dist/push.d.ts.map +1 -1
  3. package/dist/push.js +46 -1
  4. package/dist/push.js.map +1 -1
  5. package/package.json +1 -1
  6. package/plugin/README.md +0 -54
  7. package/plugin/sourcemap.json +0 -272
  8. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/Actor/AzulSync.server.luau +0 -906
  9. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/AzulService.luau +0 -573
  10. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/Config.luau +0 -31
  11. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/Enums.luau +0 -11
  12. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/Serializer.luau +0 -929
  13. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/CollapsibleTitledSection.luau +0 -214
  14. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/ColorPicker.luau +0 -360
  15. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/CustomTextButton.luau +0 -170
  16. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/DropdownMenu.luau +0 -363
  17. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/HorizontalLine.luau +0 -43
  18. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/ImageButtonWithText.luau +0 -181
  19. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledCheckbox.luau +0 -295
  20. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledColorInputPicker.luau +0 -294
  21. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledMultiChoice.luau +0 -163
  22. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledNumberInput.luau +0 -312
  23. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledRadioButton.luau +0 -55
  24. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledSlider.luau +0 -151
  25. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledTextInput.luau +0 -222
  26. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledToggleButton.luau +0 -73
  27. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/StatefulImageButton.luau +0 -125
  28. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/VerticalScrollingFrame.luau +0 -100
  29. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/VerticalSpacer.luau +0 -35
  30. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/VerticallyScalingListFrame.luau +0 -107
  31. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/GuiUtilities.luau +0 -429
  32. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/RbxGui.luau +0 -4363
  33. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/UI.luau +0 -477
  34. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/WebSocketClient.luau +0 -161
@@ -1,929 +0,0 @@
1
- local Serializer = {}
2
-
3
- local ReflectionService = game:GetService("ReflectionService")
4
- local CollectionService = game:GetService("CollectionService")
5
-
6
- local FALLBACK_SERIALIZABLE_PROPERTIES = {
7
- "Archivable",
8
- "Enabled",
9
- "RunContext",
10
- "LinkedSource",
11
- "Disabled",
12
- "Visible",
13
- "Active",
14
- "AutomaticSize",
15
- "AnchorPoint",
16
- "Position",
17
- "Rotation",
18
- "Size",
19
- "Text",
20
- "TextColor3",
21
- "TextSize",
22
- "BackgroundColor3",
23
- "BackgroundTransparency",
24
- "Image",
25
- "ImageColor3",
26
- "ImageTransparency",
27
- "LayoutOrder",
28
- "ZIndex",
29
- "AutoButtonColor",
30
- "RichText",
31
- "TextScaled",
32
- "TextWrapped",
33
- "FontFace",
34
- "Transparency",
35
- "Color",
36
- "Material",
37
- "Reflectance",
38
- "CanCollide",
39
- "CanTouch",
40
- "CanQuery",
41
- "CastShadow",
42
- "Locked",
43
- "Massless",
44
- "Shape",
45
- "SizeConstraint",
46
- "CFrame",
47
- "Orientation",
48
- "PivotOffset",
49
- "BrickColor",
50
- "TopSurface",
51
- "BottomSurface",
52
- "LeftSurface",
53
- "RightSurface",
54
- "FrontSurface",
55
- "BackSurface",
56
- "MeshId",
57
- "TextureID",
58
- "DoubleSided",
59
- "Offset",
60
- "Scale",
61
- "SoundId",
62
- "Volume",
63
- "PlaybackSpeed",
64
- "Looped",
65
- "RollOffMode",
66
- "RollOffMaxDistance",
67
- "RollOffMinDistance",
68
- "PlayOnRemove",
69
- "AnimationId",
70
- "Value",
71
- "CurrentCamera",
72
- "FieldOfView",
73
- "Ambient",
74
- "Brightness",
75
- "ClockTime",
76
- "FogColor",
77
- "FogEnd",
78
- "FogStart",
79
- "GlobalShadows",
80
- "OutdoorAmbient",
81
- "Technology",
82
- "PrimaryPart",
83
- "WorldPivot",
84
- "MetalnessMap",
85
- "NormalMap",
86
- "RoughnessMap",
87
- "ColorMap",
88
- }
89
-
90
- local FALLBACK_SERIALIZABLE_PROPERTY_SET: { [string]: boolean } = {}
91
- for _, propertyName in ipairs(FALLBACK_SERIALIZABLE_PROPERTIES) do
92
- FALLBACK_SERIALIZABLE_PROPERTY_SET[propertyName] = true
93
- end
94
-
95
- local SERIALIZABLE_PROPERTIES_CACHE: { string }? = nil
96
- local SERIALIZABLE_PROPERTY_SET_CACHE: { [string]: boolean }? = nil
97
- local writablePropertiesByClass: { [string]: { [string]: boolean } } = {}
98
- local defaultSerializedPropertiesByClass: { [string]: { [string]: any } } = {}
99
- local canonicalPropertyByLowerByClass: { [string]: { [string]: string } } = {}
100
-
101
- local function enumItemFromNumber(enumItems: { any }, value: number): any
102
- for _, item in ipairs(enumItems) do
103
- if item.Value == value then return item end
104
- end
105
- return nil
106
- end
107
-
108
- local function enumItemFromName(enumItems: { any }, name: string): any
109
- for _, item in ipairs(enumItems) do
110
- if item.Name == name then return item end
111
- end
112
- return nil
113
- end
114
-
115
- local function parseCFrameFromValue(recordValue: any): CFrame?
116
- if type(recordValue) ~= "table" then return nil end
117
-
118
- if type(recordValue.position) == "table" and type(recordValue.rotation) == "table" then
119
- local position = recordValue.position
120
- local rotation = recordValue.rotation
121
- if #position == 3 and #rotation == 9 then
122
- return CFrame.new(
123
- position[1],
124
- position[2],
125
- position[3],
126
- rotation[1],
127
- rotation[2],
128
- rotation[3],
129
- rotation[4],
130
- rotation[5],
131
- rotation[6],
132
- rotation[7],
133
- rotation[8],
134
- rotation[9]
135
- )
136
- end
137
- end
138
-
139
- if type(recordValue.components) == "table" then return CFrame.new(unpack(recordValue.components)) end
140
-
141
- return nil
142
- end
143
-
144
- local function getInstancePathSegments(instance: Instance): { string }?
145
- local path = {}
146
- local current: Instance? = instance
147
-
148
- while current and current ~= game do
149
- table.insert(path, 1, current.Name)
150
- current = current.Parent
151
- end
152
-
153
- if current ~= game then return nil end
154
- return path
155
- end
156
-
157
- local function findInstanceByPath(pathSegments: { string }): Instance?
158
- local current: Instance = game
159
- for index, segment in ipairs(pathSegments) do
160
- local nextNode: Instance? = nil
161
-
162
- if index == 1 then
163
- local ok, service = pcall(function()
164
- return game:GetService(segment)
165
- end)
166
- if ok and service then nextNode = service end
167
- end
168
-
169
- if not nextNode then nextNode = current:FindFirstChild(segment) end
170
- if not nextNode then return nil end
171
- current = nextNode
172
- end
173
-
174
- return current
175
- end
176
-
177
- function Serializer.serializeValue(
178
- value: any,
179
- options: { getInstanceRefData: ((Instance) -> { guid: string?, path: { string }? }?)? }?
180
- ): any
181
- local kind = typeof(value)
182
-
183
- if kind == "nil" then return nil end
184
- if kind == "boolean" or kind == "string" or kind == "number" then return value end
185
-
186
- if kind == "Color3" then
187
- local color = value :: Color3
188
- return { __type = "Color3", r = color.R, g = color.G, b = color.B }
189
- end
190
-
191
- if kind == "Vector2" then
192
- local vector = value :: Vector2
193
- return { __type = "Vector2", x = vector.X, y = vector.Y }
194
- end
195
-
196
- if kind == "Vector2int16" then
197
- local vector = value :: Vector2int16
198
- return { __type = "Vector2int16", x = vector.X, y = vector.Y }
199
- end
200
-
201
- if kind == "Vector3" then
202
- local vector = value :: Vector3
203
- return { __type = "Vector3", x = vector.X, y = vector.Y, z = vector.Z }
204
- end
205
-
206
- if kind == "Vector3int16" then
207
- local vector = value :: Vector3int16
208
- return { __type = "Vector3int16", x = vector.X, y = vector.Y, z = vector.Z }
209
- end
210
-
211
- if kind == "UDim" then
212
- local u = value :: UDim
213
- return { __type = "UDim", scale = u.Scale, offset = u.Offset }
214
- end
215
-
216
- if kind == "UDim2" then
217
- local u = value :: UDim2
218
- return {
219
- __type = "UDim2",
220
- xScale = u.X.Scale,
221
- xOffset = u.X.Offset,
222
- yScale = u.Y.Scale,
223
- yOffset = u.Y.Offset,
224
- }
225
- end
226
-
227
- if kind == "Rect" then
228
- local rect = value :: Rect
229
- return {
230
- __type = "Rect",
231
- minX = rect.Min.X,
232
- minY = rect.Min.Y,
233
- maxX = rect.Max.X,
234
- maxY = rect.Max.Y,
235
- }
236
- end
237
-
238
- if kind == "CFrame" then
239
- local cf = value :: CFrame
240
- return { __type = "CFrame", components = { cf:GetComponents() } }
241
- end
242
-
243
- if kind == "BrickColor" then
244
- local brick = value :: BrickColor
245
- return { __type = "BrickColor", number = brick.Number }
246
- end
247
-
248
- if kind == "EnumItem" then
249
- local enumItem = value :: EnumItem
250
- return {
251
- __type = "EnumItem",
252
- enumType = tostring(enumItem.EnumType),
253
- name = enumItem.Name,
254
- }
255
- end
256
-
257
- if kind == "NumberRange" then
258
- local range = value :: NumberRange
259
- return { __type = "NumberRange", min = range.Min, max = range.Max }
260
- end
261
-
262
- if kind == "NumberSequence" then
263
- local sequence = value :: NumberSequence
264
- local keypoints = {}
265
- for _, keypoint in ipairs(sequence.Keypoints) do
266
- table.insert(keypoints, {
267
- time = keypoint.Time,
268
- value = keypoint.Value,
269
- envelope = keypoint.Envelope,
270
- })
271
- end
272
- return { __type = "NumberSequence", keypoints = keypoints }
273
- end
274
-
275
- if kind == "ColorSequence" then
276
- local sequence = value :: ColorSequence
277
- local keypoints = {}
278
- for _, keypoint in ipairs(sequence.Keypoints) do
279
- table.insert(keypoints, {
280
- time = keypoint.Time,
281
- color = Serializer.serializeValue(keypoint.Value, options),
282
- })
283
- end
284
- return { __type = "ColorSequence", keypoints = keypoints }
285
- end
286
-
287
- if kind == "Font" then
288
- local font = value :: Font
289
- return {
290
- __type = "Font",
291
- family = font.Family,
292
- weight = font.Weight and font.Weight.Value or nil,
293
- style = font.Style and font.Style.Value or nil,
294
- }
295
- end
296
-
297
- if kind == "PhysicalProperties" then
298
- local props = value :: PhysicalProperties
299
- return {
300
- __type = "PhysicalProperties",
301
- density = props.Density,
302
- friction = props.Friction,
303
- elasticity = props.Elasticity,
304
- frictionWeight = props.FrictionWeight,
305
- elasticityWeight = props.ElasticityWeight,
306
- }
307
- end
308
-
309
- if kind == "Axes" then
310
- local axes = value :: Axes
311
- return { __type = "Axes", x = axes.X, y = axes.Y, z = axes.Z }
312
- end
313
-
314
- if kind == "Faces" then
315
- local faces = value :: Faces
316
- return {
317
- __type = "Faces",
318
- top = faces.Top,
319
- bottom = faces.Bottom,
320
- left = faces.Left,
321
- right = faces.Right,
322
- front = faces.Front,
323
- back = faces.Back,
324
- }
325
- end
326
-
327
- if kind == "Ray" then
328
- local ray = value :: Ray
329
- return {
330
- __type = "Ray",
331
- origin = Serializer.serializeValue(ray.Origin, options),
332
- direction = Serializer.serializeValue(ray.Direction, options),
333
- }
334
- end
335
-
336
- if kind == "Instance" then
337
- local instanceValue = value :: Instance
338
- local guid: string? = nil
339
- local path: { string }? = nil
340
-
341
- if options and options.getInstanceRefData then
342
- local ok, refData = pcall(function()
343
- return options.getInstanceRefData(instanceValue)
344
- end)
345
- if ok and type(refData) == "table" then
346
- if type(refData.guid) == "string" then guid = refData.guid end
347
- if type(refData.path) == "table" then path = refData.path end
348
- end
349
- end
350
-
351
- if not guid then
352
- local okGuid, debugId = pcall(function()
353
- return instanceValue:GetDebugId(0)
354
- end)
355
- if okGuid and type(debugId) == "string" and debugId ~= "" then guid = debugId end
356
- end
357
-
358
- if not path then path = getInstancePathSegments(instanceValue) end
359
-
360
- return {
361
- __type = "InstanceRef",
362
- guid = guid,
363
- path = path,
364
- }
365
- end
366
-
367
- if kind == "Region3" then
368
- local cframe = value.CFrame
369
- local size = value.Size
370
- return {
371
- __type = "Region3",
372
- min = { x = cframe.X - size.X / 2, y = cframe.Y - size.Y / 2, z = cframe.Z - size.Z / 2 },
373
- max = { x = cframe.X + size.X / 2, y = cframe.Y + size.Y / 2, z = cframe.Z + size.Z / 2 },
374
- }
375
- end
376
-
377
- if kind == "Region3int16" then
378
- local region = value :: Region3int16
379
- return {
380
- __type = "Region3int16",
381
- min = { x = region.Min.X, y = region.Min.Y, z = region.Min.Z },
382
- max = { x = region.Max.X, y = region.Max.Y, z = region.Max.Z },
383
- }
384
- end
385
-
386
- return nil
387
- end
388
-
389
- function Serializer.deserializeValue(
390
- value: any,
391
- options: {
392
- resolveInstanceByGuid: ((string) -> Instance?)?,
393
- resolveInstanceByPath: (({ string }) -> Instance?)?,
394
- }?
395
- ): any
396
- if type(value) ~= "table" then return value end
397
-
398
- local record = value :: { [string]: any }
399
- local valueType = record.__type
400
- local payload = record
401
-
402
- if type(valueType) ~= "string" then
403
- if type(record.type) ~= "string" then return value end
404
- valueType = record.type
405
- payload = record.value
406
- end
407
-
408
- if valueType == "Ref" then
409
- local guidFromRef = if type(payload) == "string" then payload else nil
410
- valueType = "InstanceRef"
411
- payload = {
412
- guid = guidFromRef,
413
- path = nil,
414
- }
415
- end
416
-
417
- if valueType == "nil" then return nil end
418
- if valueType == "Color3" then
419
- local source = if type(payload) == "table" then payload else record
420
- return Color3.new(source.r, source.g, source.b)
421
- end
422
- if valueType == "Color3uint8" then
423
- local source = if type(payload) == "table" then payload else record
424
- return Color3.fromRGB(source.r, source.g, source.b)
425
- end
426
- if valueType == "Vector2" then
427
- local source = if type(payload) == "table" then payload else record
428
- return Vector2.new(source.x, source.y)
429
- end
430
- if valueType == "Vector2int16" then
431
- local source = if type(payload) == "table" then payload else record
432
- return Vector2int16.new(source.x, source.y)
433
- end
434
- if valueType == "Vector3" then
435
- local source = if type(payload) == "table" then payload else record
436
- return Vector3.new(source.x, source.y, source.z)
437
- end
438
- if valueType == "Vector3int16" then
439
- local source = if type(payload) == "table" then payload else record
440
- return Vector3int16.new(source.x, source.y, source.z)
441
- end
442
- if valueType == "UDim" then
443
- local source = if type(payload) == "table" then payload else record
444
- return UDim.new(source.scale, source.offset)
445
- end
446
- if valueType == "UDim2" then
447
- if type(payload) == "table" and type(payload.x) == "table" and type(payload.y) == "table" then
448
- return UDim2.new(payload.x.scale, payload.x.offset, payload.y.scale, payload.y.offset)
449
- end
450
- return UDim2.new(record.xScale, record.xOffset, record.yScale, record.yOffset)
451
- end
452
- if valueType == "Rect" then
453
- if type(payload) == "table" and type(payload.min) == "table" and type(payload.max) == "table" then
454
- return Rect.new(payload.min.x, payload.min.y, payload.max.x, payload.max.y)
455
- end
456
- return Rect.new(record.minX, record.minY, record.maxX, record.maxY)
457
- end
458
- if valueType == "CFrame" then
459
- if type(record.components) == "table" then return CFrame.new(unpack(record.components)) end
460
- return parseCFrameFromValue(payload)
461
- end
462
- if valueType == "OptionalCFrame" or valueType == "OptionalCoordinateFrame" then
463
- if payload == nil then return nil end
464
- return parseCFrameFromValue(payload)
465
- end
466
- if valueType == "BrickColor" then
467
- local source = if type(payload) == "number" then payload else record.number
468
- return BrickColor.new(source)
469
- end
470
- if valueType == "EnumItem" then
471
- if type(record.enumType) ~= "string" or type(record.name) ~= "string" then return nil end
472
- local enumName = record.enumType:match("Enum%.(.+)") or record.enumType
473
- local enum = Enum[enumName]
474
- if enum then return enum[record.name] end
475
- return nil
476
- end
477
- if valueType == "Enum" then
478
- if type(payload) ~= "table" then return nil end
479
- local enumName = payload.enumType
480
- if type(enumName) ~= "string" then return nil end
481
- local enum = Enum[enumName]
482
- if not enum then return nil end
483
- if type(payload.value) == "string" then return enum[payload.value] end
484
- if type(payload.value) == "number" then return enumItemFromNumber(enum:GetEnumItems(), payload.value) end
485
- return nil
486
- end
487
- if valueType == "NumberRange" then
488
- if type(payload) == "table" then return NumberRange.new(payload.min, payload.max) end
489
- return NumberRange.new(record.min, record.max)
490
- end
491
- if valueType == "NumberSequence" then
492
- local source = record.keypoints
493
- if type(payload) == "table" and type(payload.keypoints) == "table" then source = payload.keypoints end
494
- local keypoints = {}
495
- for _, keypoint in ipairs(source or {}) do
496
- table.insert(keypoints, NumberSequenceKeypoint.new(keypoint.time, keypoint.value, keypoint.envelope or 0))
497
- end
498
- if #keypoints > 0 then return NumberSequence.new(keypoints) end
499
- return nil
500
- end
501
- if valueType == "ColorSequence" then
502
- local source = record.keypoints
503
- if type(payload) == "table" and type(payload.keypoints) == "table" then source = payload.keypoints end
504
- local keypoints = {}
505
- for _, keypoint in ipairs(source or {}) do
506
- local color = keypoint.color
507
- if type(color) == "table" and color.r ~= nil and color.g ~= nil and color.b ~= nil then
508
- color = Color3.new(color.r, color.g, color.b)
509
- else
510
- color = Serializer.deserializeValue(color, options)
511
- end
512
-
513
- if typeof(color) == "Color3" then
514
- table.insert(keypoints, ColorSequenceKeypoint.new(keypoint.time, color))
515
- end
516
- end
517
- if #keypoints > 0 then return ColorSequence.new(keypoints) end
518
- return nil
519
- end
520
- if valueType == "Font" then
521
- local source = if type(payload) == "table" then payload else record
522
- local weight = Enum.FontWeight.Regular
523
- local style = Enum.FontStyle.Normal
524
-
525
- if type(source.weight) == "number" then
526
- local matched = enumItemFromNumber(Enum.FontWeight:GetEnumItems(), source.weight)
527
- if matched then weight = matched end
528
- elseif type(source.weight) == "string" then
529
- local matched = enumItemFromName(Enum.FontWeight:GetEnumItems(), source.weight)
530
- if matched then weight = matched end
531
- end
532
-
533
- if type(source.style) == "number" then
534
- local matched = enumItemFromNumber(Enum.FontStyle:GetEnumItems(), source.style)
535
- if matched then style = matched end
536
- elseif type(source.style) == "string" then
537
- local matched = enumItemFromName(Enum.FontStyle:GetEnumItems(), source.style)
538
- if matched then style = matched end
539
- end
540
-
541
- return Font.new(source.family, weight, style)
542
- end
543
- if valueType == "PhysicalProperties" then
544
- if payload == nil then return nil end
545
- local source = if type(payload) == "table" then payload else record
546
- return PhysicalProperties.new(
547
- source.density,
548
- source.friction,
549
- source.elasticity,
550
- source.frictionWeight,
551
- source.elasticityWeight
552
- )
553
- end
554
- if valueType == "Axes" then
555
- local source = if type(payload) == "table" then payload else record
556
- return Axes.new(source.x, source.y, source.z)
557
- end
558
- if valueType == "Faces" then
559
- local source = if type(payload) == "table" then payload else record
560
- return Faces.new(source.top, source.bottom, source.left, source.right, source.front, source.back)
561
- end
562
- if valueType == "Ray" then
563
- local source = if type(payload) == "table" then payload else record
564
- local origin = Serializer.deserializeValue(source.origin, options)
565
- local direction = Serializer.deserializeValue(source.direction, options)
566
- if typeof(origin) == "Vector3" and typeof(direction) == "Vector3" then return Ray.new(origin, direction) end
567
- return nil
568
- end
569
- if valueType == "InstanceRef" then
570
- local source = if type(payload) == "table" then payload else record
571
- local guid = if type(source.guid) == "string" then source.guid else nil
572
- local path = if type(source.path) == "table" then source.path else nil
573
-
574
- if guid and options and options.resolveInstanceByGuid then
575
- local resolvedByGuid = options.resolveInstanceByGuid(guid)
576
- if resolvedByGuid then return resolvedByGuid end
577
- end
578
-
579
- if path then
580
- if options and options.resolveInstanceByPath then
581
- local resolvedByPath = options.resolveInstanceByPath(path)
582
- if resolvedByPath then return resolvedByPath end
583
- end
584
-
585
- local resolvedByDefaultPath = findInstanceByPath(path)
586
- if resolvedByDefaultPath then return resolvedByDefaultPath end
587
- end
588
-
589
- return nil
590
- end
591
- if valueType == "Region3" then
592
- local source = if type(payload) == "table" then payload else record
593
- if type(source.min) ~= "table" or type(source.max) ~= "table" then return nil end
594
- return Region3.new(
595
- Vector3.new(source.min.x, source.min.y, source.min.z),
596
- Vector3.new(source.max.x, source.max.y, source.max.z)
597
- )
598
- end
599
- if valueType == "Region3int16" then
600
- local source = if type(payload) == "table" then payload else record
601
- if type(source.min) ~= "table" or type(source.max) ~= "table" then return nil end
602
- return Region3int16.new(
603
- Vector3int16.new(source.min.x, source.min.y, source.min.z),
604
- Vector3int16.new(source.max.x, source.max.y, source.max.z)
605
- )
606
- end
607
-
608
- return nil
609
- end
610
-
611
- local function toSet(items: { string }): { [string]: boolean }
612
- local set = {}
613
- for _, item in ipairs(items) do
614
- set[item] = true
615
- end
616
- return set
617
- end
618
-
619
- local function buildSerializableProperties(): { string }
620
- if not ReflectionService then return FALLBACK_SERIALIZABLE_PROPERTIES end
621
-
622
- local classOk, classes = pcall(function()
623
- return ReflectionService:GetClasses()
624
- end)
625
-
626
- if not classOk or type(classes) ~= "table" then return FALLBACK_SERIALIZABLE_PROPERTIES end
627
-
628
- local propertySet: { [string]: boolean } = {}
629
-
630
- for _, classInfo in ipairs(classes) do
631
- local className = classInfo.Name
632
- if type(className) ~= "string" then continue end
633
-
634
- local propsOk, reflectedProperties = pcall(function()
635
- return ReflectionService:GetPropertiesOfClass(className, {
636
- ExcludeDisplay = true,
637
- })
638
- end)
639
-
640
- if not propsOk or type(reflectedProperties) ~= "table" then continue end
641
-
642
- for _, reflected in ipairs(reflectedProperties) do
643
- local reflectedName = reflected.Name
644
- if type(reflectedName) ~= "string" then continue end
645
-
646
- if
647
- reflected.Serialized == true
648
- and reflectedName ~= "Name"
649
- and reflectedName ~= "Parent"
650
- and reflectedName ~= "Source"
651
- then
652
- propertySet[reflectedName] = true
653
- end
654
- end
655
- end
656
-
657
- for _, fallbackProperty in ipairs(FALLBACK_SERIALIZABLE_PROPERTIES) do
658
- propertySet[fallbackProperty] = true
659
- end
660
-
661
- local properties = {}
662
- for propertyName in pairs(propertySet) do
663
- table.insert(properties, propertyName)
664
- end
665
-
666
- table.sort(properties)
667
-
668
- if #properties == 0 then return FALLBACK_SERIALIZABLE_PROPERTIES end
669
-
670
- return properties
671
- end
672
-
673
- function Serializer.getSerializableProperties(): { string }
674
- local cached = SERIALIZABLE_PROPERTIES_CACHE
675
- if cached then return cached end
676
-
677
- local built = buildSerializableProperties()
678
- SERIALIZABLE_PROPERTIES_CACHE = built
679
- return built
680
- end
681
-
682
- local function getSerializablePropertySet(): { [string]: boolean }
683
- local cached = SERIALIZABLE_PROPERTY_SET_CACHE
684
- if cached then return cached end
685
-
686
- local built = toSet(Serializer.getSerializableProperties())
687
- SERIALIZABLE_PROPERTY_SET_CACHE = built
688
- return built
689
- end
690
-
691
- function Serializer.getWritableSerializablePropertySetForClass(className: string): { [string]: boolean }
692
- local cached = writablePropertiesByClass[className]
693
- if cached then return cached end
694
-
695
- local serializablePropertySet = getSerializablePropertySet()
696
- local fallback = {}
697
- for propertyName in pairs(serializablePropertySet) do
698
- fallback[propertyName] = true
699
- end
700
-
701
- if not ReflectionService then
702
- writablePropertiesByClass[className] = fallback
703
- return fallback
704
- end
705
-
706
- local ok, reflectedProperties = pcall(function()
707
- return ReflectionService:GetPropertiesOfClass(className)
708
- end)
709
-
710
- if not ok or type(reflectedProperties) ~= "table" then
711
- writablePropertiesByClass[className] = fallback
712
- return fallback
713
- end
714
-
715
- local filtered = {}
716
- for _, reflected in ipairs(reflectedProperties) do
717
- local reflectedName = reflected.Name
718
- if type(reflectedName) ~= "string" then continue end
719
-
720
- if not serializablePropertySet[reflectedName] then continue end
721
-
722
- local includedByFallback = FALLBACK_SERIALIZABLE_PROPERTY_SET[reflectedName] == true
723
- if reflected.Serialized ~= true and not includedByFallback then continue end
724
-
725
- filtered[reflectedName] = true
726
- end
727
-
728
- if next(filtered) == nil then filtered = fallback end
729
-
730
- writablePropertiesByClass[className] = filtered
731
- return filtered
732
- end
733
-
734
- local function getCanonicalPropertyByLowerForClass(className: string): { [string]: string }
735
- local cached = canonicalPropertyByLowerByClass[className]
736
- if cached then return cached end
737
-
738
- local allowed = Serializer.getWritableSerializablePropertySetForClass(className)
739
- local byLower = {}
740
- for propertyName in pairs(allowed) do
741
- byLower[string.lower(propertyName)] = propertyName
742
- end
743
-
744
- canonicalPropertyByLowerByClass[className] = byLower
745
- return byLower
746
- end
747
-
748
- local function resolveWritablePropertyName(instance: Instance, propertyName: string): string?
749
- local allowed = Serializer.getWritableSerializablePropertySetForClass(instance.ClassName)
750
- if allowed[propertyName] then return propertyName end
751
-
752
- local canonicalByLower = getCanonicalPropertyByLowerForClass(instance.ClassName)
753
- return canonicalByLower[string.lower(propertyName)]
754
- end
755
-
756
- local function serializedValuesEqual(left, right): boolean
757
- if type(left) ~= type(right) then return false end
758
-
759
- if type(left) ~= "table" then return left == right end
760
-
761
- for key, value in pairs(left) do
762
- if not serializedValuesEqual(value, right[key]) then return false end
763
- end
764
-
765
- for key in pairs(right) do
766
- if left[key] == nil then return false end
767
- end
768
-
769
- return true
770
- end
771
-
772
- local function readAndSerializeProperty(
773
- instance: Instance,
774
- propertyName: string,
775
- options: {
776
- getInstanceRefData: ((Instance) -> { guid: string?, path: { string }? }?)?,
777
- }?
778
- )
779
- local ok, value = pcall(function()
780
- return (instance :: any)[propertyName]
781
- end)
782
- if not ok then return nil end
783
-
784
- return Serializer.serializeValue(value, options)
785
- end
786
-
787
- local function getDefaultSerializedPropertiesForClass(className: string): { [string]: any }
788
- local cached = defaultSerializedPropertiesByClass[className]
789
- if cached then return cached end
790
-
791
- local defaults = {}
792
- local ok, tempInstance = pcall(function()
793
- return Instance.new(className)
794
- end)
795
-
796
- if not ok or not tempInstance then
797
- defaultSerializedPropertiesByClass[className] = defaults
798
- return defaults
799
- end
800
-
801
- local allowedProperties = Serializer.getWritableSerializablePropertySetForClass(className)
802
- for propertyName in pairs(allowedProperties) do
803
- local serialized = readAndSerializeProperty(tempInstance, propertyName)
804
- if serialized ~= nil then defaults[propertyName] = serialized end
805
- end
806
-
807
- tempInstance:Destroy()
808
- defaultSerializedPropertiesByClass[className] = defaults
809
- return defaults
810
- end
811
-
812
- function Serializer.collectSerializedProperties(
813
- instance: Instance,
814
- options: {
815
- getInstanceRefData: ((Instance) -> { guid: string?, path: { string }? }?)?,
816
- }?
817
- ): { [string]: any }
818
- local properties = {}
819
- local className = instance.ClassName
820
- local allowedProperties = Serializer.getWritableSerializablePropertySetForClass(className)
821
- local defaultProperties = getDefaultSerializedPropertiesForClass(className)
822
- local serializableProperties = Serializer.getSerializableProperties()
823
-
824
- for _, propertyName in ipairs(serializableProperties) do
825
- if not allowedProperties[propertyName] then continue end
826
-
827
- if propertyName == "Name" or propertyName == "Parent" then continue end
828
- if propertyName == "Transparency" and instance:IsA("GuiObject") then continue end
829
-
830
- local serialized = readAndSerializeProperty(instance, propertyName, options)
831
- if serialized ~= nil then
832
- local defaultSerialized = defaultProperties[propertyName]
833
- if defaultSerialized == nil or not serializedValuesEqual(serialized, defaultSerialized) then
834
- properties[propertyName] = serialized
835
- end
836
- end
837
- end
838
-
839
- return properties
840
- end
841
-
842
- function Serializer.applySerializedProperties(
843
- instance: Instance,
844
- properties: { [string]: any }?,
845
- options: {
846
- resolveInstanceByGuid: ((string) -> Instance?)?,
847
- resolveInstanceByPath: (({ string }) -> Instance?)?,
848
- }?
849
- )
850
- if type(properties) ~= "table" then return end
851
-
852
- for propertyName, serialized in pairs(properties) do
853
- if propertyName ~= "Name" and propertyName ~= "Parent" and propertyName ~= "Source" then
854
- local targetPropertyName = propertyName
855
- if propertyName == "Transparency" and instance:IsA("GuiObject") then
856
- targetPropertyName = "BackgroundTransparency"
857
- end
858
-
859
- local resolvedProperty = resolveWritablePropertyName(instance, targetPropertyName)
860
- if not resolvedProperty then continue end
861
-
862
- local deserialized = Serializer.deserializeValue(serialized, options)
863
- if deserialized ~= nil then
864
- pcall(function()
865
- (instance :: any)[resolvedProperty] = deserialized
866
- end)
867
- end
868
- end
869
- end
870
- end
871
-
872
- function Serializer.applySerializedAttributes(instance: Instance, attributes: { [string]: any }?)
873
- if type(attributes) ~= "table" then return end
874
-
875
- for key, value in pairs(attributes) do
876
- pcall(function()
877
- instance:SetAttribute(key, value)
878
- end)
879
- end
880
- end
881
-
882
- function Serializer.collectSerializedTags(instance: Instance): { string }
883
- if not CollectionService then return {} end
884
-
885
- local ok, tags = pcall(function()
886
- return CollectionService:GetTags(instance)
887
- end)
888
-
889
- if not ok or type(tags) ~= "table" then return {} end
890
-
891
- if #tags > 1 then table.sort(tags) end
892
- return tags
893
- end
894
-
895
- function Serializer.applySerializedTags(instance: Instance, tags: { string }?)
896
- if type(tags) ~= "table" or not CollectionService then return end
897
-
898
- local existingTagSet: { [string]: boolean } = {}
899
- local okExisting, existing = pcall(function()
900
- return CollectionService:GetTags(instance)
901
- end)
902
- if okExisting and type(existing) == "table" then
903
- for _, tagName in ipairs(existing) do
904
- existingTagSet[tagName] = true
905
- end
906
- end
907
-
908
- local incomingTagSet: { [string]: boolean } = {}
909
- for _, tagName in ipairs(tags) do
910
- if type(tagName) == "string" and tagName ~= "" then
911
- incomingTagSet[tagName] = true
912
- if not existingTagSet[tagName] then
913
- pcall(function()
914
- CollectionService:AddTag(instance, tagName)
915
- end)
916
- end
917
- end
918
- end
919
-
920
- for existingTagName in pairs(existingTagSet) do
921
- if not incomingTagSet[existingTagName] then
922
- pcall(function()
923
- CollectionService:RemoveTag(instance, existingTagName)
924
- end)
925
- end
926
- end
927
- end
928
-
929
- return Serializer