local partStateModule = require(script.Parent.PartStateModule) local saves = {} local id_parts = {} local parts_loaded spawn(function() game.ReplicatedStorage:WaitForChild("Parts") local function added(object) if object.Parent:IsA("Folder") then local id = object:GetAttribute("PartId") if id then id_parts[id] = object end end end for _,object in pairs(_G.Storage.Parts:GetDescendants()) do added(object) end _G.Storage.Parts.DescendantAdded:Connect(added) wait(1) parts_loaded = true end) do local pairs = pairs local ipairs = ipairs local isa = Instance.new("Part").IsA local no_cframe = {} local cframe_of = function(object) if isa(object,"BasePart") then return object.CFrame elseif isa(object,"Model") then return (object.PrimaryPart or no_cframe)["CFrame"] end end local cframe_set = function(object,cframe) if isa(object,"Model") then object:SetPrimaryPartCFrame(cframe) elseif isa(object,"BasePart") then object.CFrame = cframe end end local weld_is_build = {["BuiltWeldConstraint"] = true} local insert = table.insert local typeof = typeof local is_table = {["table"]=true} local id_module = _G.Methods.Ids local colorPart = _G.Methods.colorPart local ts = tostring local tn = tonumber local format = string.format local concat = table.concat local cframe_new = CFrame.new local vector3_new = Vector3.new local table_create = table.create local form_triangle = partStateModule.form_triangle local get_nodes = partStateModule.get_nodes local apply_nodes = partStateModule.apply_nodes local apply_shift = partStateModule.apply_shift local apply_size = partStateModule.apply_size local apply_material = partStateModule.apply_material local apply_internal_welds = partStateModule.apply_internal_welds local hexxie = require(script.Parent.HexxieModule) local hd = hexxie.decode local he = hexxie.encode local hexxie_cframe_match = string.rep("(...........)",12) local hexxie_position_match = string.rep("(...........)",3) local default_cframe_format = string.rep("%s,",10).."%s" local id_attach = {} local compress_cframe_decode = function(s) if not s then return nil end local a,b,c,d,e,f,g,h,i,j,k,l = s:match(hexxie_cframe_match) return cframe_new(hd(a),hd(b),hd(c),hd(d),hd(e),hd(f),hd(g),hd(h),hd(i),hd(j),hd(k),hd(l)) end local compress_cframe_encode = function(c) local a,b,c,d,e,f,g,h,i,j,k,l = c:GetComponents() return concat({he(a),he(b),he(c),he(d),he(e),he(f),he(g),he(h),he(i),he(j),he(k),he(l)}) end local compress_position_decode = function(s) local a,b,c = s:match(hexxie_position_match) return vector3_new(hd(a),hd(b),hd(c)) end local compress_position_encode = function(v) return concat({he(v.X),he(v.Y),he(v.Z)}) end local position local http = game:GetService("HttpService") local http_json_encode = http.JSONEncode local http_json_decode = http.JSONDecode local json_decode = function(value) return http_json_decode(http,value) end local json_encode = function(value) return http_json_encode(http,value) end local ID_INDEX = 1 local CFRAMES_INDEX = 2 local EDIT_INDEX = 3 saves["4"] = { decode = function(raw,options) --print(raw) repeat wait() until parts_loaded local final = {errors = {}} local function err(text) warn(text) table.insert(final.errors,text) end options = options or {} local _compress_cframe_decode = compress_cframe_decode local _compress_position_decode = compress_position_decode if options.mirror then _compress_cframe_decode = function(str) local cf = compress_cframe_decode(str) local x,y,z = cf:ToEulerAnglesYXZ() return CFrame.fromEulerAnglesYXZ(x,-y,-z)+Vector3.new(cf.X,-cf.Y,cf.Z) end _compress_position_decode = function(str) local pos = compress_position_decode(str) return vector3_new(-pos.X,pos.Y,pos.Z) end end local compressed = raw:match("\"compress\"%:true") if compressed then raw = hexxie.rle_decode(raw) end local data = _G.Http:JSONDecode(raw) _G.Union(options,data.options or {}) local physics = options.physics local template_cframe = Instance.new("CFrameValue") local weld_template = Instance.new("WeldConstraint") local built_weld_template = weld_template:Clone() built_weld_template.Name = "BuiltWeldConstraint" local model = Instance.new("Model") final.model = model local parts = {} local editActions = { c = function(edit,part) local color = Color3.new(unpack(edit)) colorPart(part,color) end, h = function(edit,part) local hinge = part:FindFirstChild("Hinge") if hinge then hinge.CFrame = compress_cframe_decode(edit) end end, r = function(edit,part) local slider = part:FindFirstChild("Slider") if slider then slider.CFrame = compress_cframe_decode(edit) end end, m = function(edit,part) if edit then apply_material(part,Enum.Material[edit]) end end, s = function(edit,part) apply_size(part,Vector3.new(unpack(edit))) end, S = function(edit,part) apply_shift(part,edit) end, } local poly_to_internally_weld = {} local data_cframes = data.cframes local data_edits = data.edits for id,object in pairs(data.objects) do local template = id_parts[object[ID_INDEX]] if not template then err("Missing part with id "..tostring(id)) continue end local instance = template:Clone() instance.Parent = model parts[id] = instance local nodes = {} local transform_ids = object[CFRAMES_INDEX] -- BEGIN PARITY 4.1 (CAN BE REMOVED LATER) if typeof(transform_ids) ~= "table" then transform_ids = {transform_ids} end -- END PARITY 4.1 if #transform_ids == 1 then insert(nodes,compress_cframe_decode(data_cframes[transform_ids[1]])) elseif #transform_ids == 2 then insert(nodes,compress_cframe_decode(data_cframes[transform_ids[1]])) insert(nodes,hd(data_cframes[transform_ids[2]])) else for _,id in ipairs(transform_ids) do insert(nodes,compress_position_decode(data_cframes[id])) end insert(poly_to_internally_weld,instance) end apply_nodes(instance,nodes) -- Edit part if object[EDIT_INDEX] then for _,edit_id in pairs(object[EDIT_INDEX]) do local key,value = unpack(data_edits[edit_id]); (editActions[key] or function(value,instance) local edit = instance:FindFirstChild(key) if not edit then err(string.format("No key %s for %s",key,instance:GetFullName())) else if typeof(value) == "table" then value = _G.Http:JSONEncode(value) end edit.Value = value end end)(value,instance) end end -- Change anchoring if not options.physics then if instance:IsA("Model") then instance.PrimaryPart.Anchored = true else instance.Anchored = true end end end if options.physics then for _,weld in pairs(data.welds) do local w = built_weld_template:Clone() local at,bt = unpack(weld) local ap = parts[at[1]] local bp = parts[bt[1]] w.Parent = ap if ap:IsA("Model") then if at[2] then ap = ap:FindFirstChild(at[2]) or ap.PrimaryPart else ap = ap.PrimaryPart err("Missing subpart reference for weld on model A") end end if bp:IsA("Model") then if bt[2] then bp = bp:FindFirstChild(bt[2]) or bp.PrimaryPart else bp = bp.PrimaryPart err("Missing subpart reference for weld on model B") end end w.Part0 = ap w.Part1 = bp end for _,poly in ipairs(poly_to_internally_weld) do apply_internal_welds(poly) end else for _,part in pairs(model:GetDescendants()) do if part:IsA("BasePart") then part.CanCollide = false part.Anchored = true end end end local root = parts[1] if not root then error("Fatal: Missing root part; implying #parts = 0") end if root:IsA("Model") then root = root.PrimaryPart end model.PrimaryPart = root final.model = model return final end, encode = function(root,options) repeat wait() until parts_loaded local final = {errors = {}} local function err(text) warn(text) table.insert(final.errors,text) end id_module.forEachConnectedObject(root,function(object) if object:IsA("VehicleSeat") then root = object end end) final.root = root if not options then options = {} end local origin = cframe_of(root) if not origin then err("Root CFrame could not be encoded, it is probably broken") return final end local ids = {} local cframes = {} local edits = {} local welds = {} local objects = {} local data = { options = options, objects = objects, cframes = cframes, edits = edits, welds = welds } local should_optimise_cframe = not not options.optimise_cframe local cframe_epsilon = options.cframe_epsilon or 0.1 local optimise_edit = {} local optimise_cframe = {} local function cframe_id(c) local existing = optimise_cframe[c] if not existing then insert(cframes,c) local len = #cframes optimise_cframe[c] = len return len else return existing end end local function edit_id(key,value) local str if type(value) == "table" then str = json_encode(value) elseif type(value) ~="string" then str = tostring(value) else str = value end local tab = optimise_edit[key] if not tab then tab = {} optimise_edit[key] = tab end if tab[str] then return tab[str] else insert(edits,{key,value}) tab[str] = #edits return #edits end end local errors = {} local index = 0 id_module.forEachConnectedObject(root,function(object) index = index + 1 ids[object] = index local id = object:GetAttribute("PartId") or object.Name if not id then insert(errors,string.format("Part without an ID! %s",ts(object))) end -- ENCODE TRANSFORM local transform = get_nodes(object) if #transform >= 3 then for index,position in pairs(transform) do transform[index] = cframe_id(compress_position_encode(origin:PointToObjectSpace(position))) end elseif #transform == 2 then transform[1] = cframe_id(compress_cframe_encode(origin:ToObjectSpace(transform[1]))) transform[2] = cframe_id(he(transform[2])) elseif #transform == 1 then transform[1] = cframe_id(compress_cframe_encode(origin:ToObjectSpace(transform[1]))) end -- ENCODE EDITS local edits = {} for index,edit in pairs(object:GetChildren()) do if edit:HasTag("Edit") or edit:HasTag("EditHidden") then if edit:HasTag("JSON") then insert(edits,edit_id(edit.Name,json_decode(edit.Value))) else local value = edit.Value if (edit:GetAttribute("Default") ~= value) then insert(edits,edit_id(edit.Name,value)) end end end end -- ENCODE CANDID DISPLACEMENT local hinge = object:FindFirstChild("Hinge") if hinge then insert(edits,edit_id("h",compress_cframe_encode(origin:ToObjectSpace(hinge.CFrame)))) end local slider = object:FindFirstChild("Slider") if slider then insert(edits,edit_id("r",compress_cframe_encode(origin:ToObjectSpace(slider.CFrame)))) end -- ENCODE PROPERTIES local color = object:GetAttribute("Color") -- TODO HEXXIE OPTIMISE COLOUR AND SIZE if color then insert(edits,edit_id("c",{color.R,color.G,color.B})) end local material = object:GetAttribute("Material") if material then insert(edits,edit_id("m",material)) end local size = object:GetAttribute("Size") if size then insert(edits,edit_id("s",{size.X,size.Y,size.Z})) end local shift = object:GetAttribute("Shift") if shift then insert(edits,edit_id("S",shift)) end -- INSERT OBJECT local data = {id,transform} if #edits > 0 then insert(data,edits) end insert(objects,data) end) -- ENCODE WELDS for object,id in pairs(ids) do for _,weld in pairs(object:GetDescendants()) do if weld_is_build[weld.Name] then if weld.Part0 and weld.Part1 then local first = {ids[weld.Part0]} if not first[1] then first[1] = ids[weld.Part0.Parent] first[2] = weld.Part0.Name if not first[1] then err("There was a stray first weld! "..tostring(weld:GetFullName())) continue end end local second = {ids[weld.Part1]} if not second[1] then second[1] = ids[weld.Part1.Parent] second[2] = weld.Part1.Name if not second[1] then err("There was a stray second weld! "..tostring(weld:GetFullName())) continue end end insert(data.welds,{first,second}) else err("There was a dead weld!"..tostring(weld:GetFullName())) end end end end local json = _G.Http:JSONEncode(data) local result = json if options.compress then result=hexxie.rle_encode(json) end local check = hexxie.rle_decode(result) result = "v4save"..result final.data = result return final end, } end return saves