Saving Instance like a players's house doesn't work

Hey developers,
I’m trying to save player’s Data, I know how to save values like coins, level etc…

I’m looking for saving plr’s models like house.

I try to save the models into a key in the DSS but it doesn’t work, then I tried to save in tables the position, color, name etc of each parts and get all table to create a part and then give to the part the correct pos etc…
It doesn’t work at all.

Then I found something the “SaveInstance” : Player | Documentation - Roblox Creator Hub.

So I tried using it but I have an errormessage : [Cannot save a null instance]

My script is :

game.Players.PlayerAdded:Connect(function(plr)
	
	local ranchFolder = Instance.new("Model",workspace)
	ranchFolder.Name = "RanchFolder-"..plr.UserId
end)

game.Players.PlayerAdded:Connect(function(plr)
	plr:WaitForDataReady()
	plr:LoadInstance("Ranch-"..plr.UserId)
end)
	
game.Players.PlayerRemoving:Connect(function(plr)
	plr:WaitForDataReady()
	plr:SaveInstance("Ranch-"..plr.UserId,game.Workspace:FindFirstChild("Ranch-"..plr.UserId))

game:BindToClose(function()
	for _, plr in pairs(game.Players:GetPlayers()) do
		plr:WaitForDataReady()
		plr:SaveInstance("Ranch-"..plr.UserId,game.Workspace:FindFirstChild("Ranch-"..plr.UserId))
	end
end)

Player:SaveInstance is deprecated and shouldn’t be used. You should be using datastores to save things. Most likely you are going to want to save this through a table/dictionary.

Here is the dev hub for datastores: Data Stores | Documentation - Roblox Creator Hub

Here are some posts

How do you save Tables to datastore?
How to save a Dictionary to a DataStore?
Adding array to datastore?

I tried something following what you told me:
But I have an other problem and I don’t know how to fix it:

local DSS = game:GetService("DataStoreService")
local DS1 = DSS:GetDataStore("DS1")

game.Players.PlayerAdded:Connect(function(plr)
	
	local ranchFolder = Instance.new("Model",workspace)
	ranchFolder.Name = "RanchFolder-"..plr.UserId
	
	local ranchData
	local success, errormessage = pcall(function()
		ranchData = DS1:GetAsync("Ranch-"..plr.UserId)
	end)
	if success then
		if ranchData  ~= nil then
			for _, v in pairs(ranchData) do
				local newV = v:Clone()
				v.Parent = ranchFolder
			end
		end
	end
end)

	
game.Players.PlayerRemoving:Connect(function(plr)
	local partsData = { }
	
	for _, v in pairs(workspace:FindFirstChild("Ranch-"..plr.UserId):GetChildren()) do
		if v:IsA("Part") then
			table.insert(partsData,v)
		end
	end
	
	local success, errormessage = pcall(function()
		DS1:SetAsync("Ranch-"..plr.UserId,partsData)
	end)
end)

game:BindToClose(function()
	for _, plr in pairs(game.Players:GetPlayers()) do
		local partsData = { }
		
		for _,v in pairs(workspace:FindFirstChild("Ranch-"..plr.UserId):GetChildren()) do
			if v:IsA("Part") then
				table.insert(partsData,v)
			end
		end
	end
end)

You can’t save instances. You need to save their properties or something useful to help you rebuild the instance when loading.

Stuff like colour, rotation, coordinates on a grid if you have a grid system, an identifier for the house e.g. BigHouse, SmallHouse, Mansion.

1 Like

If you want to save a player’s house model, then you need to use the Serialization method, you can read mode about it here.

Also, Keep in mind that Datastores have limits. So your houses can’t be TOO big

To save an instance you need to first collect the parts cframe. If your instance is on a plot then you will need to get the parts cframe relative to the plot. To get the instances cframe relative to the plot, you must do this

local relative_CFrame = workspace.Plots.CoordinatePlane.CFrame:inverse() * instance.PrimaryPart.CFrame

CoordninatePlane would be a big large baseplate that you place your items on:

In the image above, the brown part is the baseplate.

Now, instance in my code example above is the instance that you are trying to save.

Now, what you will need to do next is add relative_CFrame to a table, but first we need something to make relative_CFrame compatible with datastores. What we will need is some kind of converter, a converter that will convert relative_CFrame into a table, and a converter to convert it back to a table.

Here are a couple of functions you can use for converting:

	function cframeToTable(cf) -- A function to convert a cframe to a table, because datastores can't store cframes unfortunately :(
		return {cf:GetComponents()};
	end
	
	function tableToCframe(t) -- A function to convert a table to a cframe
		return CFrame.new(table.unpack(t));
	end

You will need these functions. Here is how the code will look like now


function cframeToTable(cf) -- A function to convert a cframe to a table, because datastores can't store cframes unfortunately :(
	return {cf:GetComponents()};
end
	
function tableToCframe(t) -- A function to convert a table to a cframe
    return CFrame.new(table.unpack(t));
end

game:GetService("Players").PlayerRemoving:Connect(function(plr)
    local cframes = {}
    for i, v in next, house:GetChildren() do
        local relative_CFrame = workspace.Plots.CoordinatePlane.CFrame:inverse() * v.PrimaryPart.CFrame
        table.insert(cframes, {v.Name, cframeToTable(relative_CFrame)}) 
    end

    -- Save the cframes table to a datastore
end)

Now what we will need, is some code to load the player’s plot back into the game once they join.

game:GetService("Players").PlayerAdded:Connect(function(plr)
    -- Load the table from a datastore

    -- Then loop through the table and load the items into the game
    for i, v in next, loaded_Table do
        if items:FindFirstChild(v[1]) then
            local item = items:FindFirstChild(v[1]):Clone()
            item.CFrame = workspace.Plot.CoordinatePlane.CFrame * tableToCFrame(v[2])
            item.Parent = workspace.Plot.Items
        end
    end
end)

Now, you might be wondering what items is. items should be a folder either in ReplicatedStorage or ServerStorage, and the folder holds all the items that the player can place in their house.

If you need any more help / have more question, or if I forgot something please let me know! :stuck_out_tongue:

1 Like

U could just have used CFrame:ToObjectSpace() but ok

Edit: I would never have thinked to use CFrame:GetComponents() and table.unpack() to convert a CFrame to a Table and Vice-Versa.

I got some of that code from my own game, that uses that. So sorry if I didn’t use CFrame:ToObjectSpace().

u can just use tables to do so find the item’s name in the server and give it to the player this is by using tables with data store’s i never heard of SaveInstance

You should also have used Datamodel:BindToClose()

Thats because its Deprecated. It used to be a function to save a Instance into a Player when Datastores didn’t exist

This is why data store’s are useful

Size of objects shouldn’t matter?
It’s just that anything you want to save must somehow be converted into a string, and you’ve got about 260k characters.

@Abdelcrepe

When you save data you use the second argument of Instance.new(),
know that parenting the instance separately is comparatively faster. [1]

I’ve also noticed your code is very legacy like, try avoiding deprecated items wherever possible; WaitForDataReady, ::LoadInstance, ::SaveInstance all are deprecated for a good reason, Data Persistence is unreliable.

In your case: if the only possible houses are specific models then save their names in an associative array to a key unique to every player and upon a player rejoining, retrieve the data and find the particular model and load it for the player.