· 8 years ago · Nov 28, 2017, 02:32 AM
1
2local BitEncodeInstance = {}
3
4local DEBUG = false
5
6local MDClass = require(script.Parent.MDClass)
7local MDEnum = require(script.Parent.MDEnum)
8
9local CachedMDClass = {}
10
11local TypeAlias = {
12 Content = 'string';
13 ProtectedString = 'string';
14}
15
16-- How many bits to represent a given max value?
17local function GetBitWidth(maxValue)
18 return math.ceil(math.log(maxValue+1)/math.log(2))
19end
20
21-- Assign "global" IDs to the classes and properties on file
22-- ClassIdWidth = width of a classId in bits
23-- ClassIdWidthWidth = width of width of classIds
24local IdToClass = {}
25local ClassIdWidth; local ClassIdWidthWidth; do
26 local classId = 0
27 for name, class in pairs(MDClass) do
28 class.Id = classId
29 IdToClass[classId] = class
30 classId = classId + 1
31 --
32 local propId = 0
33 class.PropertyList = {}
34 for propName, prop in pairs(class.PropertyMap) do
35 prop.Id = propId
36 propId = propId + 1
37 class.PropertyList[propId] = prop
38 end
39 local propIdBits = GetBitWidth(propId-1)
40 for propName, prop in pairs(class.PropertyMap) do
41 prop.IdWidth = propIdBits
42 end
43 end
44 ClassIdWidth = GetBitWidth(classId) --7
45 ClassIdWidthWidth = GetBitWidth(ClassIdWidth) --3
46end
47
48-- Enum widths and ID mappings
49do
50 for _, enum in pairs(MDEnum) do
51 local idGen = 0
52 local valueArray = {}
53 enum.ValueToId = {}
54 enum.ValueArray = {}
55 for value, _ in pairs(enum.ValueMap) do
56 local rbxValue = Enum[enum.EnumName][value]
57 enum.ValueToId[rbxValue] = idGen
58 idGen = idGen + 1
59 enum.ValueArray[idGen] = rbxValue
60 end
61 enum.BitWidth = GetBitWidth(idGen-1)
62 end
63end
64
65-- Derived properties to ignore as they are redundant with another property
66local IgnorePropSet = {
67 ['BasePart::brickColor'] = true; -- Lowercase version of BrickColor
68 ['BasePart::CFrame'] = true; -- use position + rotation pairs
69 --['BasePart::Rotation'] = true;
70 --['BasePart::Position'] = true;
71 ['BasePart::Color'] = true; -- Redundant with BrickColor
72 ['GuiObject::Transparency'] = true; -- Redandant with other transparencies
73 ['Tool::GripUp'] = true; -- Redundant with Tool::Grip
74 ['Tool::GripRight'] = true;
75 ['Tool::GripForward'] = true;
76 ['Tool::GripPos'] = true;
77 ['Instance::Parent'] = true; -- Is inherent from data structuring
78 ['Instance::ClassName'] = true; -- Needs special encoding
79 ['Instance::archivable'] = true; -- Redundant with Archivable
80 ['FormFactorPart::formFactor'] = true; -- Lowercase version
81 --
82 ['GuiObject::BackgroundColor'] = true; -- Redundant with BackgroundColor3
83 ['GuiObject::BorderColor'] = true; -- BorderColor3
84}
85
86-- Get all of the properties of
87local function GetPropertyList(className)
88 local props = CachedMDClass[className]
89 if not props then
90 props = {}
91 local mdClass = MDClass[className]
92 for name, prop in pairs(mdClass.PropertyMap) do
93 if not prop.Writeonly and not prop.Readonly and not (prop.Security == 'PluginSecurity') then
94 local propDat = {}
95 propDat.Name = prop.PropertyName
96 local ty = prop.PropertyType
97 propDat.Type = TypeAlias[ty] or ty
98 propDat.NSName = mdClass.ClassName.."::"..prop.PropertyName
99 propDat.Property = prop
100 propDat.PropertyId = prop.Id
101 propDat.PropertyIdWidth = prop.IdWidth
102 propDat.Class = mdClass
103 propDat.ClassId = mdClass.Id
104 propDat.InstanceCount = 0
105 if not IgnorePropSet[propDat.NSName] then
106 table.insert(props, propDat)
107 end
108 end
109 end
110 baseMDClass = mdClass.BaseClassName
111 if baseMDClass then
112 for i, prop in pairs(GetPropertyList(baseMDClass)) do
113 table.insert(props, prop)
114 end
115 end
116 CachedMDClass[className] = props
117 end
118 return props
119end
120
121-- Get children
122local function GetAllChildren(o, tb)
123 table.insert(tb, o)
124 for _, ch in pairs(o:GetChildren()) do
125 GetAllChildren(ch, tb)
126 end
127end
128
129local HashValue; do
130 local HashValueLookup = {}
131 HashValueLookup['string'] = function(value) return 's_'..value end
132 HashValueLookup['float'] = function(value) return 'f_'..value end
133 HashValueLookup['double'] = function(value) return 'd_'..value end
134 HashValueLookup['int'] = function(value) return 'i_'..value end
135 HashValueLookup['bool'] = function(value) if value then return 'b_true' else return 'b_false' end end
136 HashValueLookup['Vector3'] = function(value) return 'V_'..tostring(value) end
137 HashValueLookup['Vector2'] = function(value) return '2_'..tostring(value) end
138 HashValueLookup['CoordinateFrame'] = function(value) return 'C_'..tostring(value) end
139 HashValueLookup['UDim2'] = function(value) return 'U_'..tostring(value) end
140 HashValueLookup['BrickColor'] = function(value) return 'B_'..tostring(value) end
141 HashValueLookup['Color3'] = function(value) return 'H_'..tostring(value) end
142 function HashValue(value, type, idGenerator)
143 if MDEnum[type] then
144 return 'E_'..tostring(value)
145 elseif type == 'Object' then
146 local id = idGenerator(value)
147 if id then
148 return 'I_'..id
149 else
150 return '<<NIL>>'
151 end
152 else
153 local v = HashValueLookup[type]
154 assert(v, "Bad type: "..type)
155 return v(value)
156 end
157 end
158end
159
160function BitEncodeInstance.Write(buffer, model)
161 CachedMDClass = {}
162
163 -- First, get all of the instances in the model
164 local allInstances = {}
165 GetAllChildren(model, allInstances)
166
167 -- Ignore any non-creatable instances
168 local instancesToIgnore = {}
169 do
170 local i = 1
171 while i <= #allInstances do
172 local instance = allInstances[i]
173 if MDClass[instance.ClassName].Creatable then
174 i = i + 1
175 else
176 table.remove(allInstances, i)
177 instancesToIgnore[instance] = true
178 end
179 end
180 end
181
182 -- Instance to Id lookup
183 local instanceIdGenerator = 1
184 local instanceToId = {}
185 local function generateId(instance)
186 if instance and instance:IsDescendantOf(model) then
187 local id = instanceToId[instance]
188 if not id then
189 id = instanceIdGenerator
190 instanceIdGenerator = instanceIdGenerator + 1
191 instanceToId[instance] = id
192 end
193 return id
194 else
195 return 0
196 end
197 end
198
199 -- Value buckets for type-property pairs
200 local allPropValueBucket = {}
201 local function addValue(object, prop, value)
202 if value == nil then
203 assert(prop.Type == 'Object', "Nil value for non-Instance type `"..prop.Type.."`")
204 end
205
206 -- All values for this
207 local propData = allPropValueBucket[prop.NSName]
208 if not propData then
209 propData = {
210 Prop = prop;
211 ValueMap = {};
212 InstanceCount = 0;
213 }
214 allPropValueBucket[prop.NSName] = propData
215 end
216
217 -- Increment instance count
218 propData.InstanceCount = propData.InstanceCount + 1
219
220 -- All of the objects with the given value
221 local hashedName = HashValue(value, prop.Type, generateId)
222 local valueInfo = propData.ValueMap[hashedName]
223 if valueInfo then
224 table.insert(valueInfo.ObjectList, object)
225 else
226 withValue = {ObjectList = {object}, Value = value, Prop = prop}
227 propData.ValueMap[hashedName] = withValue
228 end
229 end
230
231 -- Initial pass over the objects
232 local usedClassSet = {}
233 for _, object in pairs(allInstances) do
234 usedClassSet[MDClass[object.ClassName]] = true
235 local props = GetPropertyList(object.ClassName)
236
237 for _, prop in pairs(props) do
238 local value = object[prop.Name]
239
240 -- Add the value to the bucket set
241 addValue(object, prop, value)
242 end
243 end
244
245 -- Instance reference width
246 local instanceRefWidth = GetBitWidth(instanceIdGenerator)
247
248 -- How much space does it take to encode a given type
249 local function TypeWidth(type)
250 local enum = MDEnum[type]
251 if enum then
252 return enum.BitWidth
253 elseif type == 'string' then
254 return -1
255 elseif type == 'int' then
256 return 32
257 elseif type == 'float' then
258 return 32
259 elseif type == 'double' then
260 return 64
261 elseif type == 'bool' then
262 return 1
263 elseif type == 'Vector3' then
264 return 32*3
265 elseif type == 'Vector2' then
266 return 32*2
267 elseif type == 'CoordinateFrame' then
268 return 32*6
269 elseif type == 'UDim2' then
270 return 32*4
271 elseif type == 'BrickColor' then
272 return 6
273 elseif type == 'Color3' then
274 return 32*3
275 elseif type == 'Object' then
276 return instanceRefWidth
277 else
278 assert(false, "Bad type to TypeWidth: `"..type.."`")
279 end
280 end
281
282 -- How much space does it take to encode a given value
283 -- Differs from TypeWidth only for variable width types
284 local function GetSize(value, type)
285 if type == 'string' then
286 return 8*#value
287 else
288 return TypeWidth(type)
289 end
290 end
291
292 -- Write a value (ignoring atlasing)
293 local function WriteValue(buffer, type, value)
294 local enum = MDEnum[type]
295 if enum then
296 buffer:WriteUnsigned(enum.BitWidth, enum.ValueToId[value])
297 elseif type == 'string' then
298 buffer:WriteString(value)
299 elseif type == 'int' then
300 buffer:WriteSigned(32, value)
301 elseif type == 'float' then
302 buffer:WriteFloat32(value)
303 elseif type == 'double' then
304 buffer:WriteFloat64(value)
305 elseif type == 'bool' then
306 buffer:WriteBool(value)
307 elseif type == 'Vector3' then
308 buffer:WriteFloat32(value.X)
309 buffer:WriteFloat32(value.Y)
310 buffer:WriteFloat32(value.Z)
311 elseif type == 'CoordinateFrame' then
312 local p = value.p
313 buffer:WriteFloat32(p.X)
314 buffer:WriteFloat32(p.Y)
315 buffer:WriteFloat32(p.Z)
316 buffer:WriteRotation(value)
317 elseif type == 'BrickColor' then
318 buffer:WriteBrickColor(value)
319 elseif type == 'Color3' then
320 buffer:WriteFloat32(value.r)
321 buffer:WriteFloat32(value.g)
322 buffer:WriteFloat32(value.b)
323 elseif type == 'Vector2' then
324 buffer:WriteFloat32(value.X)
325 buffer:WriteFloat32(value.Y)
326 elseif type == 'UDim2' then
327 buffer:WriteSigned(17, value.X.Offset)
328 buffer:WriteFloat32(value.X.Scale)
329 buffer:WriteSigned(17, value.Y.Offset)
330 buffer:WriteFloat32(value.Y.Scale)
331 elseif type == 'Object' then
332 if value then
333 buffer:WriteUnsigned(instanceRefWidth, instanceToId[value])
334 else
335 buffer:WriteUnsigned(instanceRefWidth, 0)
336 end
337 end
338 end
339
340 -- How many Classes are actually used
341 local usedClassList = {}
342 local classNameToId = {}
343 local listedClassSet = {}
344 for class, _ in pairs(usedClassSet) do
345 repeat
346 table.insert(usedClassList, class)
347 listedClassSet[class] = true
348 classNameToId[class.ClassName] = #usedClassList-1
349 class = MDClass[class.BaseClassName]
350 if listedClassSet[class] then
351 break
352 end
353 until not class
354 end
355 local myClassIdWidth = GetBitWidth(#usedClassList)
356
357 -- Write out the encoding metadata
358 -- Width of my classIds
359 if DEBUG then
360 print("ClassIdWidth["..ClassIdWidthWidth.."]: "..myClassIdWidth)
361 end
362 buffer:WriteUnsigned(ClassIdWidthWidth, myClassIdWidth)
363 if DEBUG then
364 print("InstRefWidth[5]: "..instanceRefWidth)
365 end
366 buffer:WriteUnsigned(5, instanceRefWidth)
367 if DEBUG then
368 print("ClassCount["..myClassIdWidth.."]: "..#usedClassList)
369 end
370 buffer:WriteUnsigned(myClassIdWidth, #usedClassList)
371 for idPlusOne, class in pairs(usedClassList) do
372 if DEBUG then
373 print(">> Class entry, GlobalClassId["..ClassIdWidth.."]: "..class.Id.." ("..class.ClassName..")")
374 end
375 -- Which non-local classId does this correspond with
376 buffer:WriteUnsigned(ClassIdWidth, class.Id)
377 -- Now, for each property
378 local propList = GetPropertyList(class.ClassName)
379 for _, prop in pairs(propList) do
380 -- We only want non-inhereted properties here
381 if prop.Class.ClassName == class.ClassName then
382 if DEBUG then
383 print(" Writing prop:", prop.NSName)
384 end
385 --print("Adding prop "..prop.NSName.."...", "in prop list:", propList, prop, "from class:", class)
386 local modelPropData = allPropValueBucket[prop.NSName]
387 --local count = modelPropData.InstanceCount
388 -- Now, we need to decide how to encode the data for this value.
389
390 -- Create a list of the values
391 local valueList = {}
392 for _, value in pairs(modelPropData.ValueMap) do
393 table.insert(valueList, value)
394 end
395
396 -- Sort based on frequency
397 table.sort(valueList, function(a, b)
398 return #a.ObjectList > #b.ObjectList
399 end)
400
401 -- See which values to atlas
402 local atlasedValueCount = 0
403 for i, v in pairs(valueList) do
404 local valueSize = GetSize(v.Value, v.Prop.Type)
405 local valueCount = #v.ObjectList
406 -- See if atlasing the value saves memory
407 local savedMemory = valueCount * valueSize
408 local extraMemory = 1 + -- Atlas table continue flag
409 valueSize + -- Atlas table entry
410 (atlasedValueCount+2) * valueSize -- Space / entry to encode this atlased value
411 if savedMemory > extraMemory then
412 -- We should atlas this
413 atlasedValueCount = atlasedValueCount + 1
414 v.Atlased = true
415 v.AtlasId = atlasedValueCount
416 end
417 end
418
419 -- Now, decide the mode:
420 --[[
421 0: Only one value, just write it here and omit it from each item
422 1: Some but not all values are atlased, use 0: Inline value, 111[...]110: atlased value.
423 x: All values are atlased. TODO: Huffman Encode
424 --]]
425 assert(#valueList > 0, "Value list should have at least one value, since property is present")
426 if #valueList == 1 then
427 if DEBUG then
428 print(" Is single -> "..tostring(valueList[1].Value))
429 end
430 prop.Mode = 'Single'
431 buffer:WriteBool(false)
432 if prop.NSName == 'BasePart::Rotation' then
433 buffer:WriteRotation(valueList[1].ObjectList[1].CFrame)
434 else
435 WriteValue(buffer, prop.Type, valueList[1].Value)
436 end
437 --print("Space used:", GetSize(valueList[1].Value, prop.Type) + 1)
438 else
439 if DEBUG then
440 print(" Is Atlased, count="..atlasedValueCount)
441 end
442 prop.Mode = 'Atlas'
443 buffer:WriteBool(true)
444 local totalMem = {}
445 for i = 1, atlasedValueCount do
446 local value = valueList[i]
447 buffer:WriteBool(true)
448 if prop.NSName == 'BasePart::Rotation' then
449 buffer:WriteRotation(value.ObjectList[1].CFrame)
450 else
451 WriteValue(buffer, value.Prop.Type, value.Value)
452 end
453 end
454 buffer:WriteBool(false)
455 end
456 end
457 end
458 end
459 --COPIED FROM MAEL
460 local timertick = tick()
461 local threshold = 2
462 local lasttick = tick()
463 local tickn = 0
464
465
466 -- Write the objects themselves
467 local function WriteObject(buffer, object)
468 --print("")
469 if DEBUG then
470 print("Writing object:", object:GetFullName())
471 end
472 -- Write the class name
473 buffer:WriteUnsigned(myClassIdWidth, classNameToId[object.ClassName])
474 -- Write out the Id if this object is referenced
475 if instanceToId[object] then
476 buffer:WriteBool(true)
477 buffer:WriteUnsigned(instanceRefWidth, instanceToId[object])
478 else
479 buffer:WriteBool(false)
480 end
481 -- Write out the properties
482 local propList = GetPropertyList(object.ClassName)
483 for _, prop in pairs(propList) do
484 if prop.Mode == 'Atlas' then
485 local modelPropData = allPropValueBucket[prop.NSName]
486 local valueInfo = modelPropData.ValueMap[HashValue(object[prop.Name], prop.Type, generateId)]
487 if valueInfo.Atlased then
488 for i = 1, valueInfo.AtlasId do
489 buffer:WriteBool(true)
490 end
491 buffer:WriteBool(false)
492 else
493 buffer:WriteBool(false)
494 if prop.NSName == 'BasePart::Rotation' then
495 buffer:WriteRotation(object.CFrame)
496 else
497 WriteValue(buffer, valueInfo.Prop.Type, valueInfo.Value)
498 end
499 end
500 elseif prop.Mode == 'Single' then
501 -- nothing to do
502 else
503 assert(false, 'unreachable, bad prop mode: '..tostring(prop.Mode).." on prop "..prop.NSName.." ("..tostring(prop)..")")
504 end
505 end
506 -- Write out the children
507 for _, ch in pairs(object:GetChildren()) do
508 if not instancesToIgnore[ch] then
509 buffer:WriteBool(true)
510 WriteObject(buffer, ch)
511 --ADDED TO COPY MAEL
512 if tick() - lasttick > threshold then
513 lasttick = tick()
514 wait(1)
515 tickn=tickn+1
516 print(tickn)
517 end
518 end
519 end
520 buffer:WriteBool(false) -- end of child list
521 end
522 WriteObject(buffer, model)
523
524-- -- Dump the value lists
525-- for nsName, propData in pairs(allPropValueBucket) do
526-- print("Prop:", nsName)
527-- for hashedValue, valueInfo in pairs(propData.ValueMap) do
528-- print(" "..#valueInfo.ObjectList..": ["..hashedValue.."]")
529-- end
530-- print("")
531-- end
532 if DEBUG then
533 print(#allInstances.." -> "..#txt.."("..string.format('%.1f', #txt/#allInstances).." characters/instance)")
534 end
535end
536
537function BitEncodeInstance.Read(buffer)
538 CachedMDClass = {}
539 --
540 local myClassIdWidth = buffer:ReadUnsigned(ClassIdWidthWidth)
541 if DEBUG then
542 print("ClassIdWidth["..ClassIdWidthWidth.."]: "..myClassIdWidth)
543 end
544 local instanceRefWidth = buffer:ReadUnsigned(5)
545 if DEBUG then
546 print("InstRefWidth[5]: "..instanceRefWidth)
547 end
548 local usedClassCount = buffer:ReadUnsigned(myClassIdWidth)
549 if DEBUG then
550 print("ClassCount["..myClassIdWidth.."]: "..usedClassCount)
551 end
552 --
553 -- Read a value (ignoring atlasing)
554 local function ReadValue(buffer, type)
555 local enum = MDEnum[type]
556 if enum then
557 return enum.ValueArray[buffer:ReadUnsigned(enum.BitWidth) + 1]
558 elseif type == 'string' then
559 return buffer:ReadString()
560 elseif type == 'int' then
561 return buffer:ReadSigned(32)
562 elseif type == 'float' then
563 return buffer:ReadFloat32()
564 elseif type == 'double' then
565 return buffer:ReadFloat64()
566 elseif type == 'bool' then
567 return buffer:ReadBool()
568 elseif type == 'Vector3' then
569 local x = buffer:ReadFloat32()
570 local y = buffer:ReadFloat32()
571 local z = buffer:ReadFloat32()
572 return Vector3.new(x, y, z)
573 elseif type == 'CoordinateFrame' then
574 local px = buffer:ReadFloat32()
575 local py = buffer:ReadFloat32()
576 local pz = buffer:ReadFloat32()
577 local rot = buffer:ReadRotation()
578 return CFrame.new(px, py, pz) * rot
579 elseif type == 'BrickColor' then
580 return buffer:ReadBrickColor()
581 elseif type == 'Color3' then
582 local r = buffer:ReadFloat32()
583 local g = buffer:ReadFloat32()
584 local b = buffer:ReadFloat32()
585 return Color3.new(r, g, b)
586 elseif type == 'Vector2' then
587 local x = buffer:ReadFloat32()
588 local y = buffer:ReadFloat32()
589 return Vector2.new(x, y)
590 elseif type == 'UDim2' then
591 local xo = buffer:ReadSigned(17)
592 local xs = buffer:ReadFloat32()
593 local yo = buffer:ReadSigned(17)
594 local ys = buffer:ReadFloat32()
595 return UDim2.new(xs, xo, ys, yo)
596 elseif type == 'Object' then
597 local v = buffer:ReadUnsigned(instanceRefWidth)
598 if v == 0 then
599 return nil
600 else
601 return v
602 end
603 end
604 end
605 --
606 local usedClassList = {}
607 for i = 1, usedClassCount do
608 --print("")
609 --print(">> Class entry")
610 --
611 local classId = buffer:ReadUnsigned(ClassIdWidth)
612 if DEBUG then
613 print("Read ClassId:", classId)
614 end
615 local class = IdToClass[classId]
616 if DEBUG then
617 print("GlobalClassId["..ClassIdWidth.."]: "..class.Id.." ("..class.ClassName..")")
618 end
619 table.insert(usedClassList, class)
620 --
621 local propList = GetPropertyList(class.ClassName)
622 for _, prop in pairs(propList) do
623 if prop.Class.ClassName == class.ClassName then
624 if buffer:ReadBool() then
625 -- Atlased
626 prop.Mode = 'Atlas'
627 prop.ValueAtlas = {}
628 local atlasId = 1
629 while buffer:ReadBool() do
630 -- Atlased values follows
631 if prop.NSName == 'BasePart::Rotation' then
632 prop.ValueAtlas[atlasId] = buffer:ReadRotation()
633 else
634 prop.ValueAtlas[atlasId] = ReadValue(buffer, prop.Type)
635 end
636 atlasId = atlasId + 1
637 end
638 --print("Atlased, count="..(atlasId-1))
639 else
640 -- Single
641 prop.Mode = 'Single'
642 if prop.NSName == 'BasePart::Rotation' then
643 prop.Value = buffer:ReadRotation()
644 else
645 prop.Value = ReadValue(buffer, prop.Type)
646 end
647 --print("Single -> "..tostring(prop.Value))
648 end
649 end
650 end
651 end
652 --
653 local instanceIdToInstance = {}
654 local instanceRefsToPatch = {}
655 --
656 --COPIED FROM MAEL
657 local timertick = tick()
658 local threshold = 2
659 local lasttick = tick()
660 local function ReadObject(buffer)
661 -- Write the class name
662 local classId = buffer:ReadUnsigned(myClassIdWidth)
663 local class = usedClassList[classId+1]
664 --print("")
665 if DEBUG then
666 print("Reading object: <"..class.ClassName..">")
667 end
668 local object = Instance.new(class.ClassName)
669 --
670 if buffer:ReadBool() then
671 local id = buffer:ReadUnsigned(instanceRefWidth)
672 instanceIdToInstance[id] = object
673 end
674 --
675 local propList = GetPropertyList(object.ClassName)
676 local deferredProps = {}
677 for _, prop in pairs(propList) do
678 if DEBUG then
679 print(" Reading prop:", prop.NSName, "(", prop, ")")
680 end
681 if prop.Mode == 'Atlas' then
682 local atlasId = 0
683 while buffer:ReadBool() do
684 atlasId = atlasId + 1
685 end
686 --
687 local value;
688 if atlasId == 0 then
689 if prop.NSName == 'BasePart::Rotation' then
690 value = buffer:ReadRotation()
691 else
692 value = ReadValue(buffer, prop.Type)
693 end
694 else
695 value = prop.ValueAtlas[atlasId]
696 end
697 if DEBUG then
698 print(" Value:", value)
699 end
700 if prop.Type == 'Object' then
701 table.insert(instanceRefsToPatch, {
702 Object = object;
703 Property = prop.Name;
704 Value = value;
705 })
706 elseif prop.NSName == 'BasePart::Rotation' then
707 object.CFrame = CFrame.new(object.Position) * value
708 elseif prop.NSName == 'BasePart::Size' then
709 -- Size must be deferred until FormFactor and Shape have been assigned
710 -- Pretty annoying edge case to have to deal with.
711 -- AFAIK this is the only property-value-dependency that exists.
712 table.insert(deferredProps, {
713 Property = prop.Name;
714 Value = value;
715 })
716 else
717 object[prop.Name] = value
718 end
719 elseif prop.Mode == 'Single' then
720 if prop.Type == 'Object' then
721 table.insert(instanceRefsToPatch, {
722 Object = object;
723 Property = prop.Name;
724 Value = prop.Value;
725 })
726 elseif prop.NSName == 'BasePart::Rotation' then
727 object.CFrame = CFrame.new(object.Position) * prop.Value
728 else
729 if DEBUG then
730 print(" Value:", prop.Value)
731 end
732 object[prop.Name] = prop.Value
733 end
734 else
735 assert(false, 'unreachable, bad prop mode: '..tostring(prop.Mode).." on prop "..prop.NSName.." ("..tostring(prop)..")")
736 end
737 end
738 for _, prop in pairs(deferredProps) do
739 object[prop.Property] = prop.Value
740 end
741 --
742 while buffer:ReadBool() do
743 local ch = ReadObject(buffer)
744 ch.Parent = object
745 --ADDED TO COPY MAEL
746 if tick() - lasttick > threshold then
747 lasttick = tick()
748 wait(1)
749 --tickn=tickn+1
750 --print(tickn)
751 end
752 end
753 return object
754 end
755 local root = ReadObject(buffer)
756 for _, patchRef in pairs(instanceRefsToPatch) do
757 if DEBUG then
758 print("Patching Object Ref:", patchRef.Object:GetFullName().."::"..patchRef.Property.." = "..tostring(instanceIdToInstance[patchRef.Value]))
759 end
760 patchRef.Object[patchRef.Property] = instanceIdToInstance[patchRef.Value]
761 end
762 return root
763end
764
765return BitEncodeInstance