Fast Effective Part Serializing

simple perfomant part serializing test.

results:
Processing: image.png…

test place:
MansionModelTest.rbxl (104.8 KB)

code:

-- note about the Serialize func:
-- im not ■■■■■■ its just that copying and paste some code will skip some additional bytecodes at the compiled script
local partFormat = "I1I2I1I1I1fffffffff"
local stringPack = string.pack
local function Serialize(model: Model)
	local serializedData = ""
	for i, v in model:GetDescendants() do
		if v:IsA("Part") then
			local color = v.Color
			local size = v.Size
			local position = v.Position
			local orientation = v.Orientation
			local specialMesh = v:FindFirstChildOfClass("SpecialMesh")
			serializedData ..= stringPack(partFormat,
				if specialMesh and specialMesh.TextureId == "" then 0 else v.Shape.Value,
				v.Material.Value,
				color.R*255, color.G*255, color.B*255,
				size.X, size.Y, size.Z,
				position.X, position.Y, position.Z,
				orientation.X, orientation.Y, orientation.Z
			)
		elseif v:IsA("WedgePart") then
			local color = v.Color
			local size = v.Size
			local position = v.Position
			local orientation = v.Orientation
			serializedData ..= stringPack(partFormat,
				3,
				v.Material.Value,
				color.R*255, color.G*255, color.B*255,
				size.X, size.Y, size.Z,
				position.X, position.Y, position.Z,
				orientation.X, orientation.Y, orientation.Z
			)
		elseif v:IsA("CornerWedgePart") then
			local color = v.Color
			local size = v.Size
			local position = v.Position
			local orientation = v.Orientation
			serializedData ..= stringPack(partFormat,
				4,
				v.Material.Value,
				color.R*255, color.G*255, color.B*255,
				size.X, size.Y, size.Z,
				position.X, position.Y, position.Z,
				orientation.X, orientation.Y, orientation.Z
			)
		end
	end

	return serializedData
end

local stringUnpack = string.unpack
local partDataSize = 
	1 + -- Shape takes 1 byte (I1)
	2 + -- Material takes 2 bytes (I2)
	3 + -- Color takes 3 bytes (I1I1I1)
	(4 * 3) * 3

print(`bytes per part: {partDataSize}`)

local function Unserialize(serializedData: string)
	local dataLenght = #serializedData
	local i = 1
	local j = 1
	local model = Instance.new("Model")
	while j < dataLenght do
		j = i + partDataSize
		local shapeValue,
		materialValue,
		colorR, colorG, colorB,
		sizeX, sizeY, sizeZ, 
		positionX, positionY, positionZ,
		orientationX, orientationY, orientationZ = stringUnpack(partFormat,
			serializedData:sub(i, j)
		)

		local part = Instance.new("Part")
		part.Shape = Enum.PartType:FromValue(shapeValue)
		part.Material = Enum.Material:FromValue(materialValue)
		part.Color = Color3.fromRGB(colorR, colorG, colorB)
		part.Size = Vector3.new(sizeX, sizeY, sizeZ)
		part.Position = Vector3.new(positionX, positionY, positionZ)
		part.Orientation = Vector3.new(orientationX, orientationY, orientationZ)
		part.Anchored = true
		part.Parent = model

		i = j
	end

	return model
end

warn("Serializing:")

local start = os.clock()
local serializedData = Serialize(workspace.Mansion)

local elapsed = os.clock() - start
print(`elapsed: {elapsed}`)

local serializedDataKB = #serializedData/1024
print(`serialized data size: {serializedDataKB}kb`)
print(`| size with ZLib lv.5 compression: ~{serializedDataKB/4.75}kb`) -- I think zlib level 5 would reduce something between 80% of the size


warn("Deserializing:")

local start = os.clock()
local model = Unserialize(serializedData)

local elapsed = os.clock() - start
print(`elapsed: {elapsed}`)

local dataLenght = #serializedData
print(`parts: {dataLenght/partDataSize}`)

model.Parent = workspace

forgoted to mention that:
it serializes to uft8 so copying and pasting wont really work, you would need to encode it first.

2 Likes

color is already using only 3 bytes.
the position and size with float values already give perfect results for the intended use; i mean, it’s way too precise already.

1 Like

I’m sorry, I have misunderstood a bit how string.pack works with table.concat, you are right about it.

Sorry for that, don’t wanted to sound like that.

1 Like