Saving furniture with my current system?

  1. What do you want to achieve?
  • I want to save furniture in my current saving housing system.
    So , first you need to know that players can own multiple houses. If they purchase a house, they’ll recieve a Objectvalue with the houseName as the name
game.ReplicatedStorage.Events.HouseShop:WaitForChild("PurchaseHouse").OnServerInvoke = function(player,houseName)
	local money = player.leaderstats.Money
	local house = game.ServerStorage.House:FindFirstChild(houseName) -- Object of the Item they want to buy
	
	if house then	--if the item exist

		if game.ServerStorage.PlayerData[player.Name].House:FindFirstChild(houseName) == nil then
		if money.Value >= house.Price.Value then
			money.Value = money.Value - house.Price.Value -- reduce the money
			
			local itemValue = Instance.new("ObjectValue")
			itemValue.Name = houseName
			itemValue.Parent = game.ServerStorage.PlayerData[player.Name].House
	
			return true
		else
			return "NotEnoughMoney"
		end
		end
	end
end

For the saving, I save all the children in the playerfolder (playerData in my example, located in serverstorage ).

pcall(function()
		local house = game.ServerStorage.PlayerData[player.Name].House:GetChildren()
		local houseTable = {}
		
		for _, v in pairs(house) do
			table.insert(houseTable,v.Name)
		end
		houseDataStore:SetAsync(player.UserId.."-house",houseTable)
	end)

If the player joins the game, I simply do this to copy the owned housemodels (via there names) in the playerData folder so he can use the objects to spawn the house later.

if house_data then
		for _, house in pairs(house_data) do
			if game.ServerStorage.House:FindFirstChild(house) then
				local houseClone = game.ServerStorage.House[house]:Clone()
				houseClone.Parent = houseData
			end
		end
	else
		print("No house data")
	end

So thats how I save my cars and houses. Now the question comes on how I would save furniture and the position relativly to the plot the house is placed.
Like I said, Players can own multiple houses so each furniture needs to be for the dedicated house. I don’t want players to place furniture in HouseA and if they spawn HouseB later, the furniture from HouseA is placed in HouseB.

Information you need to know on how I spawn the houses.
I have multiple plots in the world where players can go and interact with. if they interact with they will be displayed a UI.


if they click on a house, they’ll send a event with the houseName and the housePosition ( this is the plotPosition ).

After this I’ll simply run this

game.ReplicatedStorage.Events.HouseShop:WaitForChild("SpawnHouse").OnServerEvent:Connect(function(player,houseName,housePosition)

	local house = game.ServerStorage.House:FindFirstChild(houseName):Clone()
	house.Name = player.Name.."-House"
	house.Parent = game.Workspace
	house:SetPrimaryPartCFrame(housePosition.Parent.HouseSpawner.CFrame)
	game.Workspace.Building:FindFirstChild(housePosition.Parent.Name).Owner.Value = player.Name
	game.Workspace:FindFirstChild(player.Name.."-House").Owner.Value = player.Name
end)

So I believe you know everything you need to know now to help me out figuring out a furniture system.

Basicly I want players to be able to place furniture on there plot down, but it saves relativly to the house they spawned.

Maybe I could combine this with the very good tutorial I saw earlier : Creating A Furniture Placement System
but I don’t really know how because he is using OOP.

If you have any questions I’ll try to answer them!

Any help is appreciated! :))

~J

3 Likes

You would need a data store that stores the players specific data.

1 Like

Can you explain a little bit more?
I already got datastore that stores player specific data.

1 Like

So for example, every time the player places down the furniture e.g. the game autosaves it, and it will store data on what furniture it is and what location it is in.

1 Like

Thats obvious isnt it?
The question is how to do it with the specific system? So the furniture gets saved for each house diffrently.

2 Likes

Well you would have to save it into the house with a local player.

1 Like

Save it in the house with a local player? I don’t fully understand.
As described above, I am saving the Name of the house, not actually models.

The scripts detects that the player has a HouseA string and copies the HouseA Model with the same name as the string ( thats the same for everyone ) to his folder in serverstorage.

1 Like

Yes, but you will need all the models to save and then put that into a group, rename it to what the player names it and then it saves in a data store.

Is that even possible to save the models?

put all the models in a folder in replicated storage and then save the name of model and its object space cframe in relation to some sort of placement area on the house

1 Like

Okey but how would I go about furniture only saving for the specific house?
So HouseA has furniture thats placed down in HouseA and houseB has furniture that has been placed down in HouseB

what i would do is have one datastore entry that houses the specific houses along with the keys linked to its specific data

It’d be helpful if a response was provided in bulk on how to do this rather than inflating the thread with replies.

When it comes to furnishing in a house, you will always want to work with references as the overall data size may get very large. To do this, make sure you have a specific partition in your DataStore, whether it’s under your main store as a dictionary with the key House or a separate DataStore.

Make an indicator of which house the player is currently editing, it will serve as your first partitioning look up. In the DataStore lookup, you should ideally have a table that represents the selected house as well as the furniture that had been placed in that house.

When it comes to saving actual furniture, you cannot save models, so you should save data about them. Usually, furniture is saved as two values: a numerical id and the positioning. Other configurations apply. The id is used to associate a smaller character amount with a game asset that could have more characters (e.g. Basic Chair I is id 12). It’s up to you how to break that down.

2 Likes

Thanks for the first good reply ^^
I was thinking the same about the responds.

My knockledge about datastore is not the best, I even think its kinda outdated?
You might wanna check that out.

--Variables
local dataStores = game:GetService("DataStoreService"):GetDataStore("PlayerDataStore")
local carDataStore = game:GetService("DataStoreService"):GetDataStore("CarDataStore")
local houseDataStore = game:GetService("DataStoreService"):GetDataStore("HouseDataStore")
local tuneDataStore = game:GetService("DataStoreService"):GetDataStore("TuneDataStore")

local playersLeft = 0

game.Players.PlayerAdded:connect(function(player)
	
	playersLeft = playersLeft + 1
	
	local stats = Instance.new("IntValue")
	stats.Name = "leaderstats"
	stats.Parent = player
	
	local money = Instance.new("IntValue")
	money.Name = "Money"
	money.Parent = stats
	
	
	--Special folder to save SPECIAL player Data
	local playerData = Instance.new("Folder")
	playerData.Name = player.Name
	playerData.Parent = game.ServerStorage.PlayerData
	
	local carData = Instance.new("Folder")
	carData.Name = "Cars"
	carData.Parent = playerData
	
	local houseData = Instance.new("Folder")
	houseData.Name = "House"
	houseData.Parent = playerData
	
	local tuneData = Instance.new("Folder")
	tuneData.Name = "TuneParts"
	tuneData.Parent = playerData
	
	local player_data
	local car_data
	local house_data
	local tune_data
	
	--Pcall ist eine normale funktion wie function, nur das, falls ein fehler auftritt es nicht das komplette script stoppt sondern nur die funktion.
	pcall(function()
		player_data = dataStores:GetAsync(player.UserId.."-leaderstats") --Saves the player general leaderstats
	end)
	
	pcall(function()
		car_data = carDataStore:GetAsync(player.UserId.."-cars")	--Saves the houses
	end)
	
	pcall(function()
		house_data = houseDataStore:GetAsync(player.UserId.."-house")
	end)
	
	pcall(function()
		tune_data = tuneDataStore:GetAsync(player.UserId.."-tune")
	end)
	
		
	if player_data ~= nil then
		--Wenn der Spieler schon Data hat
		money.Value = player_data
	else
		--Wenn der Spieler noch keine Daten hat ( zum ersten mal joined )
		money.Value = 1000
	end
	
	if car_data then
		for _, car in pairs(car_data) do
			if game.ServerStorage.Cars:FindFirstChild(car) then
				local carClone = game.ServerStorage.Cars[car]:Clone()
				carClone.Parent = carData
				end
			end
		else
			print("No car data")
	end
	

	
	if house_data then
		for _, house in pairs(house_data) do
			if game.ServerStorage.House:FindFirstChild(house) then
				local houseClone = game.ServerStorage.House[house]:Clone()
				houseClone.Parent = houseData
			end
		end
	else
		print("No house data")
	end
	
end)

local bindableEvent = Instance.new("BindableEvent")

game.Players.PlayerRemoving:Connect(function(player)
	
	pcall(function()
		dataStores:SetAsync(player.UserId.."-leaderstats",player.leaderstats.Money.Value)
	end)
	
	pcall(function()
		local cars = game.ServerStorage.PlayerData[player.Name].Cars:GetChildren()
		print(#cars)
		local carsTable = {}
		
		for _, v in pairs(cars) do
			table.insert(carsTable,v.Name)
		end
		carDataStore:SetAsync(player.UserId.."-cars",carsTable)
	end)
	
	pcall(function()
		local house = game.ServerStorage.PlayerData[player.Name].House:GetChildren()
		local houseTable = {}
		
		for _, v in pairs(house) do
			table.insert(houseTable,v.Name)
		end
		houseDataStore:SetAsync(player.UserId.."-house",houseTable)
	end)
	
	
	print("Data Saved!")
	playersLeft = playersLeft - 1
	bindableEvent:Fire()
end)

game:BindToClose(function()
	while playersLeft > 0 do
		bindableEvent.Event:Wait()
	end
end)
1 Like

There are a couple of issues with that code in terms of practice. Preferably this would be more appropriate for a Code Review thread since the topic is more geared towards asking how to save furnishing with housing, but I’ll go ahead and drop a review anyhow.


I. Excessive DataStore usage.

You currently have 4 DataStores, which amounts to 4 requests per player per action in regards to save data. You should always aim to work with less DataStores and look into consolidation.

I don’t know the exact structure of your DataStores but you can definitely flatten them out. For example, if the cars DataStore is only used fot determining what cars a user owns, you can use associative ids and put them in the PlayerDataStore instead.

II. Excessive variable redundancy.

In all your cases of fetching DataStores, you use GetService. You should always aim to hold things you intend to use several times in variables. Services especially. Make sure to also use GetService for any service fetching as it is canonically correct and promotes codebase consistency.

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")

III. Misuse of BindToClose.

I believe that this specific technique is used to attempt to prolong when a server closes. This will not work and it is bad practice as well. It’d be more advisable to run all your operations immediately across all existing data sets.

Due to these kinds of matters where players may not exist while the server is trying to close, I never use the player as a data dependency. I always put their data in ServerStorage or ReplicatedStorage. In a BindToClose function, I run through those folders and save accordingly.

Keeping player instances and servers alive just to save data is not recommended. Save and finish everything up ASAP, then let the server go down.

IV. Use catcher functions. Especially for DS work.

In the case that you have a yielding function at the top of your script, PlayerAdded may not fire for everyone. For this reason, you should keep catcher functions in mind to account fot new and existing players.

local function onPlayerAdded(player)
    -- code
end

Players.PlayerAdded:Connect(onPlayerAdded)

for _, player in ipairs(Players:GetPlayers()) do
    onPlayerAdded(player)
end

V. Excessive variables for pcalls.

Small rant: this is about the fourth time I’ve had to address pcalls. I’m now convinced I need to write the tutorial on them this week.

Anyway. The excess of local variables you created for pcalls is wholly unnecessary. Pcall is a callback so you have one of two options: return the value from within the pcall or wrap the function call directly.

local success, result = pcall(function ()
    return DataStore:GetAsync("KEY")
end)

local success, result = pcall(DataStore.GetAsync, DataStore, "KEY")

Make sure not to throw away the success values. In the case of DataStore endpoints being down or calls not being successful, you should block saving or retry fetching data. You should never adopt the paradigm of “if the value exists, set it, otherwise ignore it and use a default”.

VI. If you are iterating through an array and don’t need the key, use ipairs.

More of a practice-based thing than anything but ipairs is designed to work with keys, while pairs is for dictionaries. In the case that you have only values in an array, you should always opt to use ipairs.


's about it. I typed this in mobile by the way so errors or missing information are almost guaranteed.

4 Likes

Alright! I’ll adress most of the stuff and improve it :))
in your First topic you said smth about putting the ID’s in the playerdatastore for the cars.
Right now I am saving the cars the same way how I save the houses, by name. So how would I do it with ID’s and how would I get that into the Datastore from the playerdata?

And your fifth topic isnt really understandable for me. Aren’t pcalls simply functions that when an error occures not break the whole script? I probably got that wrong.

Anyways, Thanks :))
didnt help me out much regarding furniture but will improve my code ^^

The concept of saving by id is relatively the same as saving by name. Instead of saving the full name of something, you would save it by a very short identifier (a few numbers typically, 4 or so). You would save, load and work with these IDs in all instances of your game.

With an id based system, you’d have a ModuleScript that is meant to associate an id with an object via key-value entries. So when you perform a lookup on index 57 (the id), you get the value “Red Car” (the literal object name).

Example:

-- somewhere here, we save an id as 57. later:
local currentCar = ids.Cars[57]
print(currentCar) -- Red Car

The purpose of pcalls are to run functions in protected mode. When your calls error, a success of false is returned as a variable plus an error message. Your script won’t terminate though, it’ll keep running. This is unlike a non-pcalled function call which your script stops working after an error.

pcall is more than just preventing scripts from stopping when functions error: they allow you to do custom error handling. You can decide what the script does when a call errors, rather than abiding by automatic handling which is to push the error to the console and then stop the script.

2 Likes

Now I got it :))
I’ll try that with the module script and adapt it to my script.

Now I understand pcalls aswell, good explaination.

Here is how you can do that you must save each model like furniture in relationshipt to a part in the center of the house so in Roblox each part also have its own rotation so use

CFrame:ToWorldSpace()

with that you can for example make a sword part spawn directly behind a player if he picks it up

I suggest you carefully read a test what the following tutorial gives.
https://developer.roblox.com/en-us/articles/Understanding-CFrame

2 Likes

Thanks, I’ll look into that :))