local module = {}
_G.Methods = module

local tags = _G.Tags

local saveToPartTranslator = {
	Barrel = "$B",
	Sheet = "$S",
	Stick = "$s"
}
local partToSaveTranslator = {
	
}

local ids = require(_G.Modules.IdModule)
module.Ids = ids
local mouse = require(_G.Modules.MouseModule)
module.Mouse = mouse

local buildParts = {}
local idParts = {}
local idNodes = {}
local polyParts = {}
module.canLoad = false
module.polyParts = polyParts
module.buildParts = buildParts
module.idParts = idParts
task.spawn(function()
	require(game.ReplicatedFirst.ReadyModule)("Parts")
	_G.Storage:WaitForChild("Parts")
	for _,object in pairs(_G.Storage.Parts:GetDescendants()) do
		if object.Parent:IsA("Folder") then
			local id = object:GetAttribute("PartId")
			if not id then continue end
			if not object:HasTag("ConnectorObject") then
				buildParts[object.Name] = object
				if saveToPartTranslator[object.Name] then buildParts[saveToPartTranslator[object.Name]] = object end
				idParts[id] = object
			else
				if object:GetAttribute("Nodes") == 3 then
					if not object:FindFirstChild("A") then
						warn(tostring(object).." missing sub-wedge 'A'")
					end
					if not object:FindFirstChild("B") then
						warn(tostring(object).." missing sub-wedge 'B'")
					end
				end
				idParts[id] = object
				polyParts[id] = object
				polyParts[object.Name] = object
				--[[if object:FindFirstChild("A") and object:FindFirstChild("B") then
					polyParts[object.Name] = object
					polyParts[id] = object
				elseif object:FindFirstChild("1") and object:FindFirstChild("2") then
					idParts[id] = object
				else
					polyParts[object.Name] = object
					polyParts[id] = object
					idParts[id] = object
				end]]
			end
		end
	end
	-- To fix old compatibility issues
	local polyCompat = {
		Pane = 17,
		Wedge = 6,
		Stick = 5,
		Paper = 20,
		HeavySheet = 10,
		Bar = 13,
		LightSheet = 14,
		Rod = 13,
		Rope = 22
	}
	for index,item in pairs(polyCompat) do
		--[[local part = idParts[item]:Clone()
		part.Parent = idParts[item].Parent
		if part:FindFirstChild("1") then part["1"].Name = "A" end
		if part:FindFirstChild("2") then part["2"].Name = "B" end]]
		polyParts[index] = polyParts[item]
	end
	module.canLoad = true
end)
for id,part in pairs(idParts) do
	local nodeId = part:GetAttribute("NodeId")
	if nodeId then
		idNodes[id] = idParts[nodeId]
	end
end
function module.getPartByName(name)
	return buildParts[name]
end
function module.getPolyByName(name)
	return polyParts[name]
end
function module.getPartById(id)
	return idParts[id]
end
function module.getNodeById(id)
	return idNodes[id]
end

function module.setItemCFrame(item,cframe)
	if item:IsA("BasePart") then
		item.CFrame = cframe
	elseif item:IsA("Model") then
		if item.PrimaryPart then
			local mcframe,size = item:GetBoundingBox()
			item:SetPrimaryPartCFrame(cframe-cframe.Position+cframe:PointToWorldSpace(mcframe:PointToObjectSpace(item.PrimaryPart.Position)))
		end
	end
	return cframe
end

function module.getItemSize(item)
	if item:IsA("BasePart") then
		return item.Size
	elseif item:IsA("Model") then
		if item.PrimaryPart then
			return item:GetExtentsSize()
		end
	end
end

function module.getItemCFrame(item)
	if item:IsA("BasePart") then
		return item.CFrame
	elseif item:IsA("Model") then
		return item:GetBoundingBox()
	end
end

function module.colorPart(part,color)
	if part:IsA("Model") then
		for _,subpart in pairs(part:GetDescendants()) do
			if subpart:IsA("BasePart") then
				subpart.Color = color
			end
		end
	else
		part.Color = color
	end
	part:SetAttribute("Color",color)
end

function module.shapeTriangle(p1,p2,p3,r1,r2) -- simple function, might work?
	local s = 0 --0.01
	local bottom = {
		b1 = nil,
		b2 = nil,
		dist = 0,
		other = nil
	}
	local function check(point1,point2,other)
		local dist = (point1 - point2).Magnitude
		if dist > bottom.dist then
			bottom.b1 = point1
			bottom.b2 = point2
			bottom.other = other
			bottom.dist = dist
		end
	end
	check(p1,p2,p3) -- check all of the different options for bottom and get the right one?
	check(p1,p3,p2)
	check(p2,p3,p1)
	local view = CFrame.lookAt(bottom.b1,bottom.b2,(bottom.other-bottom.b1).Unit)
	local object = view:PointToObjectSpace(bottom.other)
	local length = object.Z
	local height = object.Y
	r1.Size = Vector3.new(r1.Size.X,height-s,-length-s)
	r1.CFrame = (view * CFrame.Angles(0,math.rad(180),0)) * CFrame.new(0,height/2+s/2,-length/2+s/2)
	r2.Size = Vector3.new(r2.Size.X,height-s,-(-bottom.dist-length)-s)
	r2.CFrame = view * CFrame.new(0,height/2+s/2,-(bottom.dist-length)/2+s/2)
	return r1,r2
end

function module.positionPart(part,nodes,s)
	local d = (nodes[1].Position - nodes[2].Position).Magnitude -- distance
	local a = nodes[1].Position -- first node
	local b = nodes[2].Position -- second node
	if #nodes == 2 then -- stick on Z (XY thick)
		part.CFrame = CFrame.lookAt(a,b) * CFrame.Angles(0,math.pi*0.5,0)
		part.CFrame = part.CFrame + part.CFrame.RightVector * d * 0.5
		part.Size = Vector3.new(d,part.Size.Y,part.Size.Z)
	end
	if #nodes == 3 then -- triangle on YZ (X thick)
		local c = nodes[3].Position -- third node
		local p2 = part["B"]
		local p1 = part["A"]
		module.shapeTriangle(a,b,c,p1,p2)
	end
end

function module.shiftPart(part,shift)
	shift = shift - (part:GetAttribute("Shift") or 0)
	for _,part in pairs(part:GetChildren()) do
		if part:IsA("BasePart") then 
			part.Position = part.Position + part.CFrame.RightVector * shift
		end
	end
end

function module.getNodePositions(part)
	
end

function module.form_rod(pa,p1,p2)
	local d = (p1 - p2).Magnitude -- distance
	pa.CFrame = CFrame.lookAt(p1,p2) * CFrame.Angles(0,math.pi*0.5,0)
	pa.CFrame = pa.CFrame + pa.CFrame.RightVector * d * 0.5
	pa.Size = Vector3.new(d,pa.Size.Y,pa.Size.Z)
end

module.saves = require(script.Parent:WaitForChild("SerialisationModule"))

spawn(function()
	require(game.ReplicatedFirst.ReadyModule)("MethodCommands")
	if _G.IsClient then
		local commands = require(_G.Modules.CommandsModule)
		commands.new("save format (%d)","set save format <number:string>: Sets current save version. Use '4'",function(v)
			_G.Events.Notification:Fire("Set Save Format","Set save format to: "..v)
			_G.SaveFormat = v
		end)
		commands.new("save comp[^%s]- (.+)","set save compress <on:bool>: Enables or disables save compression for debugging. A bad idea.",function(v)
			v = not not v:match("t")
			_G.Events.Notification:Fire("Set Save Compress","Set save compress to: "..tostring(v))
			_G.SaveCompression = v
		end)
	end
end)

function module.saves.modelToData(root)
	if root then
		if module.saves[_G.SaveFormat] then
			local success,result = xpcall(module.saves[_G.SaveFormat].encode,function(err)
					print("before")
					_G.Error(string.format("Save V%s Error: %s",_G.SaveFormat,err))
				end,root,{
				compress = _G.SaveCompression
			})
			if not success then return nil end
			if #result.errors > 0 then print(result.errors) end
			return result.data
		else
			return _G.Methods.dataToString(_G.Methods.modelToTable(root))
		end
	else
		error("Root passed is nil!",2)
	end
end

function module.saves.dataToModel(raw,physics,mirror)
	if _G.IsString(raw) then 
		local v,r = raw:match("v(%d)save(.+)")
		if v then
			local success,result = xpcall(module.saves[v].decode,function(err)
					warn(debug.traceback(err))
				end,r,{
				physics = physics,
				mirror = mirror,
			})
			if not success then return nil end
			if #result.errors > 0 then print(result.errors) end
			if result.model then result.model:SetAttribute("Version","V"..v) end
			return result.model
		else
			local tabular = _G.Protected(module.universalConvertToTable,raw)
			if not tabular or not tabular[1] then return nil end
			local model = _G.Protected(module.convertToModel,tabular,physics)
			if not model then return nil end
			if not tabular[1]:match("V%dSAVE") then
				tabular.Name = tabular[1]
				--model:AddTag("Old")
			end
			model:AddTag("Old")
			return model
		end
	else
		return nil
	end
end

function module.tableToModel(data,physics)
	
end

function module.stringToData(str)
	if type(str) ~= "string" then error("Not a string",2) end
	local insert = table.insert
	if str:sub(1,1) ~= "{" then return end
	local function parse(start)
		local data = {}
		local function register(str)
			if #str < 1 then return end
			local index,part = str:match("(.*)=(.*)")
			if not index then
				insert(data,str)
			else
				data[index] = str
			end
		end
		while start do
			local first,last = str:find("[{},]",start+1,false)
			local c = str:sub(last,last)
			if c == "{" then
				local sub_data
				local index = str:sub(start+1,last-2)
				sub_data,start = parse(last)
				if #index > 0 then
					data[index] = sub_data 
				else
					insert(data,sub_data)
				end
			elseif c == "}" then
				register(str:sub(start+1,last-1))
				return data,last
			else
				if c == "," then
					register(str:sub(start+1,last-1))
				end
				start = last
			end
		end
		wait()
	end
	return parse(1)
end

function module.can_destroy(part,player)
	return part:HasTag("Combat") or part.Parent:HasTag("Combat")
end

local built = {BuiltWeldConstraint = true}
function module.break_part(part,player)
	if part:IsA("Model") then
		for _,item in pairs(part:GetDescendants()) do
			if built[item.Name] then
				item:Destroy()
			end
		end
	elseif part:IsA("BasePart") then
		part:BreakJoints()
	end
end

function module.wash_part(part,colour,material)
	if part:IsA("Model") then
		for _,item in pairs(part:GetDescendants()) do
			if part:IsA("BasePart") then
				part.Color = colour
				part.Material = material
			end
		end
	elseif part:IsA("BasePart") then
		part.Color = colour
		part.Material = material
	end
end

function module.fire(root,player)
	if module.can_destroy(root,player) then
		if root:HasTag("Engine") then
			local fire = _G.Storage.Fire:Clone()
			fire.Parent = root.PrimaryPart
		end
		module.wash_part(root,Color3.new(1, 0.831617, 0.298161),Enum.Material.Neon)
		wait(math.random(1,10)*0.333)
		module.break_part(root,player)
		for _,part in pairs(root:GetTouchingParts()) do
			module.fire(part,player)
		end
		wait(math.random(1,20)*0.333)
		module.wash_part(root,Color3.new(0,0,0),Enum.Material.Sand)
	end
end

function module.explode(root,player,power)
	print("explode")
	if root:HasTag("Exploded") then return end
	root:AddTag("Exploded")
	local debris = game:GetService("Debris")
	if not power then power = 10 end
	local explosion = _G.Sounds.Explosion:Clone()
	explosion.Parent = root
	explosion:Destroy()
	local burn = _G.Storage.Burn:Clone()
	burn.Parent = root
	debris:AddItem(burn,1)
	
	local params = OverlapParams.new()
	params.MaxParts = 40
	params.RespectCanCollide = true
	params.FilterType = Enum.RaycastFilterType.Include
	params.FilterDescendantsInstances = {game.Workspace.Objects}
	local result = game.Workspace:GetPartBoundsInRadius(root.Position,10,params)
	
	wait()
	for _,hit in pairs(result) do
		if module.can_destroy(hit,player) then
			module.break_part(hit,player)
			if math.random(1,3) == 1 then module.fire(hit,player) end
		end
	end
	
	wait(0.1)
	for _,hit in pairs(result) do
		hit:ApplyImpulse((hit.Position-root.Position)*-100)
	end
end

function module.playerOwnsPart(player,part) return part:HasTag("P_"..player.UserId) or part:HasTag("P_ublic") end

function module.dataToString(data)
	local insert = table.insert
	local isString = {["string"] = true}
	local isNumber = {["number"] = true}
	local isTable = {["table"] = true}
	local function converts(t)
		local result = {"{"}
		local first = true
		for index,item in pairs(t) do
			if not first then insert(result,",") else first = false end
			local value
			if isTable[type(item)] then
				value = converts(item)
			else
				value = tostring(item)
			end
			if isNumber[type(index)] then
				insert(result,value)
			elseif isString[type(index)] then
				insert(result,index.."="..value)
			end 
		end
		insert(result,"}")
		return table.concat(result)
	end
	return converts(data)
end

function module.convertToData(data)
	if not data:match("{.*}") then return end
	data = data:gsub("%s","")
	local index = 2
	local function parseTable(t)
		local this = {} -- The table we're working with now
		local idx -- Special named table index
		local _,last
		local char
		local value
		while true do
			_,last = data:find("[{,=}]",index)
			if not last then return end
			char = data:sub(last,last) -- The next significant character
			if index ~= last then value = data:sub(index,last-1) end -- Current value in table
			index = last + 1
			--print(t,value,char)
			local function terminate()
				if idx then this[idx] = value -- If there is an index, add it by index
				else table.insert(this,value) end -- Otherwise just plainly insert it (by order)
			end
			if char == "=" then -- When finding an '=', take the existing piece and make it an index
				idx = value
			elseif char == "," then -- When finding a ',', the entry has terminated so add it
				terminate()
			elseif char == "{" then -- When finding a '{', there is a new table whose value needs to be computed
				value = parseTable(t+1)
			else-- char == "}" then -- When finding a '}', this table has terminated so return
				terminate()
				return this
			end
		end
	end
	return parseTable(1)
end

function module.universalConvertToTable(data)
	if not (data:match("{V%dSAVE,objects=")) then return module.stringToData(data)
	else return module.convertToData(data) end
end

function module.removeSpaces(data)
	local new = ""
	for index = 1, #data do
		local char = string.sub(data,index,index)
		if char ~= " " then new = new .. char end
	end
	return new
end
function module.round(number,decimals)
	local pow = math.pow(10,decimals)
	return math.round(number*pow)/pow
end
function module.makeSaveString(data)
	local saveDataString = "{"..data.saveName..","
	local saveObjectsString = "{"
	for index,item in ipairs(data.saveObjects) do
		local editString = ""
		local cframeString = module.removeSpaces(tostring(item.CFrame))
		if item.Edit then 
			for _,edit in pairs(item.Edit) do
				editString = editString .. "{"..edit[1]..","..edit[2].."},"
			end
		end
		if #editString ~= 0 then editString = "{"..editString.."}," end
		saveObjectsString = saveObjectsString .. "{{"
			.. cframeString
			.. "}" .. ","
			.. item.Name .. ","..editString.."},"
	end
	saveObjectsString = saveObjectsString .. "}"
	saveDataString = saveDataString .. saveObjectsString .. ","
	local saveWeldsString = "{"
	for index,item in pairs(data.saveWelds) do
		saveWeldsString  = saveWeldsString .. "{{"
			.. tostring(item.Part0[1])
		if item.Part0[2] then
			saveWeldsString = saveWeldsString .. "," .. item.Part0[2] 
		end
		saveWeldsString  = saveWeldsString .. "},{"
			.. tostring(item.Part1[1])
		if item.Part1[2] then
			saveWeldsString = saveWeldsString .. "," .. item.Part1[2]
		end
		saveWeldsString = saveWeldsString .. "}},"
	end
	saveWeldsString = saveWeldsString .. "}"
	saveDataString = saveDataString .. saveWeldsString
	saveDataString = saveDataString .. "}"
	--print(saveDataString)
	return saveDataString
end

function module.convertValue(value) -- BRUH
	if value == "true" then return true
	elseif value == "false" then return false
	elseif tonumber(value) then return tonumber(value)	
	else return value end 
end
function module.tableToCFrame(c)
	local n = tonumber
	return CFrame.new(
		n(c[1]),n(c[2]),n(c[3]),
		n(c[4]),n(c[5]),n(c[6]),
		n(c[7]),n(c[8]),n(c[9]),
		n(c[10]),n(c[11]),n(c[12])
	)
end

local function weld(part1,part2)
	local weld = Instance.new("WeldConstraint")
	weld.Name = "BuiltWeldConstraint"
	weld.Parent = part1
	weld.Part0 = part1
	weld.Part1 = part2
	return weld
end
local function weldNoPersist(part1,part2)
	weld(part1,part2).Name = "WeldConstraint"
end
local CFrameNew = CFrame.new
local CFrameFromAxisAngle = CFrame.fromAxisAngle
local tableUnpack = table.unpack
local Vector3New = Vector3.new
local n = tonumber
function module.cframeToData(cframe)
	local p = cframe.Position
	local r,w = cframe:ToAxisAngle()
	return {{cframe.Position},{r},w}
end
local tp = table.pack
function module.cframeToDataNoAxis(cframe)
	return tp(cframe:GetComponents())
end
local nc = CFrame.new
local tu = table.unpack
function module.dataToCFrameNoAxis(data)
	return nc(tu(data))
end
function module.dataToCFrame(data)
	if not (data[1] and data[2] and data[3]) then return CFrame.new() end
	local p1,p2,p3 = tableUnpack(data[1])
	local r1,r2,r3 = tableUnpack(data[2])
	local w = data[3]
	return CFrameNew(n(p1),n(p2),n(p3))*CFrameFromAxisAngle(Vector3New(n(r1),n(r2),n(r3)),n(w))
end

local editCompress = {Color="C",HingeCFrame="HC",Size="S"}
local editDepress = {HC="HingeCFrame",C="Color",S="Size"}
function module.modelToTable(root)
	local getItemCFrame = function(object) if object:IsA("BasePart") then return object.CFrame elseif object:IsA("Model") then return object.PrimaryPart.CFrame end end 
	local origin = getItemCFrame(root)
	local insert = table.insert
	local saveData = {"V3SAVE"}
	saveData.objects = {}
	saveData.welds = {}
	local objects = {}
		--[[Agreed object structure:
		{
			1: name
			2: {node1,node2,node3} cframes (nodes included)
			3: {colour={a,b,c},size={a,b,c},speed=120} edits (size included)
		}
		]]
	local isBuiltWeld = {["BuiltWeldConstraint"] = true}
	local index = 1
	ids.forEachConnectedObject(root,function(object)
		insert(objects,object)
		objects[object] = index
		index = index + 1
		local cframes = {}
		local edits = {}
		local nodes = object:GetAttribute("Nodes")
		if nodes then
			for i=1,nodes do
				insert(cframes,module.cframeToDataNoAxis(origin:ToObjectSpace(object[i].CFrame)))
			end
		else
			insert(cframes,module.cframeToDataNoAxis(origin:ToObjectSpace(getItemCFrame(object))))
		end
		-- edits
		for index,edit in pairs(object:GetChildren()) do
			if tags:HasTag(edit,"Edit") and
				(edit:GetAttribute("Default") ~= edit.Value)
			then
				edits[editCompress[edit.Name] or edit.Name] = tostring(edit.Value)
			end
		end
		local id = object:GetAttribute("PartId") or object.Name
		--local original = module.getPartById(id) or module.getPartByName(id)
		-- psuedo edits
		local color = object:GetAttribute("Color")
		if color then edits[editCompress["Color"]] = {color.R,color.G,color.B} end
		local hinge = object:FindFirstChild("Hinge")
		if hinge then edits[editCompress["HingeCFrame"]] = module.cframeToDataNoAxis(origin:ToObjectSpace(hinge.CFrame)) end
		local size = object:GetAttribute("Size")
		if size then size = size * math.sqrt(0.333); edits[editCompress["Size"]] = {size.X,size.Y,size.Z} end
		
		-- final object
		local final = {id,cframes}
		if next(edits) then insert(final,edits) end
		insert(saveData.objects,final)
	end)

	for index,object in ipairs(objects) do
		for _,weld in pairs(object:GetDescendants()) do
			if isBuiltWeld[weld.Name] then
				if not weld.Part0 or not weld.Part1 then continue end
				local first = objects[weld.Part0]
				if not first then first = {objects[weld.Part0.Parent],weld.Part0.Name}
				else first = {first} end
				local second = objects[weld.Part1]
				if not second then second = {objects[weld.Part1.Parent],weld.Part1.Name}
				else second = {second} end
				insert(saveData.welds,{first,second})
			end
		end 
	end
	
	return saveData
end

function module.convertToModel(data,physics)
	if not data then return end
	local v = data[1]:match("V(%d)SAVE")
	if not v or not data.objects then return module.oldConvertToModel(data,physics) end
	local insert = table.insert
	local objects = {}
	local model = Instance.new("Model")
	local toCFrame = module.dataToCFrameNoAxis
	local setCFrame = function(part,cframe) if part:IsA("BasePart") then part.CFrame = cframe else part:SetPrimaryPartCFrame(cframe) end end
	model:SetAttribute("Version","V3")
	if v == "2" then setCFrame = module.setItemCFrame toCFrame = module.dataToCFrame model:SetAttribute("Version","V2") end
	local editActions = {
		Color = function(edit,part)
			module.colorPart(part,Color3.new(n(edit[1]),n(edit[2]),n(edit[3])))
		end,
		HingeCFrame = function(edit,part)
			local hinge = part:FindFirstChild("Hinge")
			if hinge then hinge.CFrame = toCFrame(edit) end
		end,
		MaterialAttribute = function()

		end,
		Size = function(edit,part)
			local size = Vector3.new(n(edit[1]),n(edit[2]),n(edit[3]))
			if part:IsA("Model") then
				part:ScaleTo(size.Magnitude)
				size = (size/math.sqrt(0.333)) -- For parity with the new stronger system
			else
				part.Size = size
			end
			part:SetAttribute("Size",size)
		end,
	}
	repeat wait() until module.canLoad
	for index,object in ipairs(data.objects) do
		local name = object[1]
		name = n(name) or name
		local isPoly = #object[2] > 1
		local part = idParts[name]
		local node
		if not part then
			if isPoly then
				part = module.getPolyByName(name)
				if not part then print(name) end
				node = part.Parent:FindFirstChild("Node") or idParts[-1]
			else part = module.getPartByName(name) end
		else
			if isPoly then
				node = idNodes[name] or part.Parent:FindFirstChild("Node") or idParts[-1]
			end
		end
		if not part then warn(name,isPoly,idParts,buildParts,polyParts) end
		part = part:Clone()
		local isModel = not part:IsA("BasePart")
		local primary = part
		if isModel then primary = part.PrimaryPart end
		if isPoly then
			local nodes = {}
			for index,cframe in pairs(object[2]) do
				local newNode = node:Clone()
				newNode.CFrame = toCFrame(cframe)
				newNode.Parent = part
				newNode.Name = tostring(index)
				newNode.Transparency = 1
				nodes[index] = newNode
			end
			module.positionPart(part,nodes)
			for _,subpart in pairs(part:GetChildren()) do if subpart ~= primary and subpart:IsA("BasePart") then weldNoPersist(subpart,primary) end end 
		else
			setCFrame(part,toCFrame(object[2][1]))
		end
		local cnv = module.convertValue
		if object[3] then
			for index,edit in pairs(object[3]) do
				if editDepress[index] then index = editDepress[index] end
				local value = part:FindFirstChild(index)
				if not value then
					if editActions[index] then
						editActions[index](edit,part)
					end
				else 
					value.Value = cnv(edit)
				end
			end
		end
		if not physics then 
			if isModel then
				for _,object in pairs(part:GetChildren()) do
					if object:IsA("BasePart") then
						object.Anchored = true
						object.CanCollide = false
					end
				end
			else
				part.CanCollide = false
				part.Anchored = true
			end
		end
		if index == 1 then if part:IsA("Model") then model.PrimaryPart = part.PrimaryPart else model.PrimaryPart = part end end
		if part.Name == "VehicleSeat" then model.PrimaryPart = part.PrimaryPart end
		part.Parent = model
		insert(objects,part)
	end
	if physics then 
		for index,weld in pairs(data.welds) do
			local weld11 = n(weld[1][1])
			local weld12 = weld[1][2]
			local weld21 = n(weld[2][1])
			local weld22 = weld[2][2]
			local part1 = objects[weld11]
			local part2 = objects[weld21]
			if weld12 then part1 = part1:FindFirstChild(weld12) or part1 end
			if weld22 then part2 = part2:FindFirstChild(weld22) or part2 end
			if not (part1 and part2) then print("Weld Missing",objects[weld11],objects[weld21],weld21,weld22) continue end
			local new = Instance.new("WeldConstraint")
			if part1:IsA("Model") then part1 = part1.PrimaryPart end
			if part2:IsA("Model") then part2 = part2.PrimaryPart end
			new.Part0 = part1
			new.Part1 = part2
			new.Parent = part1
			new.Name = "BuiltWeldConstraint"
		end
	end
	return model
end

function module.oldConvertToModel(data,physics)
	if not data[1] then return end
	
	local parts = {}
	local n = tonumber

	local currentModel = Instance.new("Model")
	currentModel:SetAttribute("Version","V1")
	
	local internalSuccess = true
	local internalValue = {}
	local success,value = xpcall(function()
		for index,part in pairs(data[2]) do
			local success,value = pcall(function()
				local isPoly = #part[1] == 0
				local new = module.getPartByName(part[2])
				if new then new = new:Clone() end
				if isPoly then
					new = module.getPolyByName(part[2])
					if not new then warn("Missing Poly: "..part[2]) end
					local old = new
					new = new:Clone()
					local realNodes = {}
					local attach = new
					if new:IsA("Model") then attach = new.PrimaryPart end
					for _,edit in pairs(part[3]) do
						if #edit[1] == 1 and tonumber(edit[1]) then
							local realNode = (old.Parent:FindFirstChild("Node") or _G.Storage.Parts.DefaultNode):Clone()
							realNode.CFrame = module.tableToCFrame(edit[2])
							realNode.Name = edit[1]
							realNode.Parent = new 
							realNode.Transparency = 1
							table.insert(realNodes,realNode)
						end
					end
					module.positionPart(new,realNodes)
					for _,node in pairs(realNodes) do
						weldNoPersist(node,attach)
					end
					for _,part in pairs(new:GetChildren()) do
						if part:IsA("BasePart") and part ~= attach then weldNoPersist(part,attach) end
					end
				end
				if new then
					table.insert(parts,new)
					new.Parent = currentModel
					if index == 1 then 
						if new:IsA("Model") then 
							currentModel.PrimaryPart = new.PrimaryPart else currentModel.PrimaryPart = new 
						end
					end
					if part[3] then
						local cnv = module.convertValue
						if typeof(part[3]) == "string" then print(part[3]) end
						for _,edit in pairs(part[3]) do
							if #edit ~= 1 and not tonumber(edit[1]) and not (edit[1] == "C") and new:FindFirstChild(edit[1]) then
								new[edit[1]].Value = module.convertValue(edit[2])
							elseif (edit[1] == "C") then
								module.colorPart(new,Color3.new(cnv(edit[2][1]),cnv(edit[2][2]),cnv(edit[2][3])))
							end
						end
					end
					if not physics then
						if new:IsA("Model") then
							new.PrimaryPart.Anchored = true
							for _,part in pairs(new:GetDescendants()) do
								if part:IsA("BasePart") then part.CanCollide = false part.CanTouch = false end
							end
						else
							new.Anchored = true
							new.CanCollide = false
							new.CanTouch = false
						end
					end
					if not isPoly then
						module.setItemCFrame(new,module.tableToCFrame(part[1]))
					end
				end
			end)
			if not success then internalSuccess = false table.insert(internalValue,value) end
		end
		if not physics then return currentModel,data end

		for index,weld in pairs(data[3]) do
			local new = Instance.new("WeldConstraint")
			local part0 = parts[n(weld[1][1])]
			if not part0 then warn("Weld missing.") continue end
			if weld[1][2] and part0:FindFirstChild(weld[1][2]) then part0 = part0[weld[1][2]] end
			local part1 = parts[n(weld[2][1])]
			if not part1 then continue end
			if weld[2][2] and part1:FindFirstChild(weld[2][2]) then part1 = part1[weld[2][2]] end
			if part0:IsA("Model") then part0 = part0.PrimaryPart end
			if part1:IsA("Model") then part1 = part1.PrimaryPart end
			new.Part0 = part0
			new.Part1 = part1
			new.Parent = part0
			new.Name = "BuiltWeldConstraint"
		end
	end,print)
	
	if not internalSuccess then data[1] = "CORRUPTED" for _,item in pairs(internalValue) do print(data) print(item) debug.traceback(item,2) end end
	
	--print(data)

	return currentModel,data
end

local function getConnectedParts(part)
	local connected = part:GetConnectedParts(false)
	for _,joint in pairs(part:GetJoints()) do
		if joint:IsA("Constraint") then
			if joint.Attachment0 and joint.Attachment1 then 
				if joint.Attachment0.Parent == part then 
					table.insert(connected,joint.Attachment1.Parent)	
				else
					table.insert(connected,joint.Attachment0.Parent)	
				end
			end
		end
	end
	return connected
end

function module.forEachConnectedObject(start,callback,show,parent)
	if not start then return end
	if start:IsA("Model") then start = start.PrimaryPart end

	if not parent then parent = game.Workspace.Objects end

	local parts = {}
	local objects = {}

	local function checkPart(part)
		local object
		if part.Parent:IsA("Model") and part.Parent.Parent == parent then
			object = part.Parent
		elseif part.Parent == parent then
			object = part
		end
		if not object or objects[object] then return end
		objects[object] = object
		if callback then callback(object) end
	end

	local function tryConnected(part)
		if parts[part] then return else parts[part] = part checkPart(part) end
		local connected = getConnectedParts(part)
		for _,part in pairs(connected) do
			tryConnected(part)
		end
	end

	tryConnected(start)

	return objects
end

function module.setNetworkOwner(part,player)
	for _,part in pairs(part:GetConnectedParts(true)) do
		if part.Anchored then
			return
		end
	end
	part:SetNetworkOwner(player)
end

function module.getOwnedOrNil(player,part)
	if module.playerOwnsPart(player,part) then
		return part
	elseif module.playerOwnsPart(player,part.Parent) then
		return part
	end
	return nil
end

function module.getObjectFromPart(part)
	if part:GetAttribute("PartId") then 
		return part
	elseif part.Parent and part.Parent:GetAttribute("PartId") then
		return part.Parent
	end
end

module.getMouseHit = mouse.getMouseHit

return module
