How do i create a saving system for parts using DataStores?

Title pretty much explains it all. I’m trying to create a saving and loading system that takes specific parts from the workspace, and save them, or load them. All parts are unanchored, and some parts have other objects inside them, such as scripts, values, and click detectors. I have no experience with datasore, but i do know that is the only kind of service i can use to create a saving and loading system. Some help will be much appreciated!

8 Likes

You can put them into ServerStorage while you aren’t using them, and bring them back into Workspace when you want them.

Nevermind, after @Invoke_Client’s response I understand the question better.

1 Like

You would have to use DataStoreService storing the Position of parts in a table :slight_smile:

Using the SetAsync function to save the parts position

Using the GetAsync function to load the positions of the parts.

3 Likes

haven’t even used datestores before, i have no idea how they work. Could you possibly provide an example?

1 Like

Here is an Example, using RemoteEvents

Loading the data

remotes.SaveData.OnServerEvent:Connect(function(player, name, pX, pY, pZ)
local oldData = {}
table.insert(oldData, {Id = name, Position = {X = pX, Y = pY, Z = pZ}})
if dataStore:GetAsync(player.UserId) ~= nil then
	for i,v in pairs(dataStore:GetAsync(player.UserId)) do
		table.insert(oldData, v)
	end
	dataStore:SetAsync(player.UserId, oldData)
else
	dataStore:SetAsync(player.UserId, oldData)
end
end)

Saving the data

game.Players.PlayerAdded:Connect(function(plr)
for i,v in pairs(dataStore:GetAsync(plr.UserId)) do
	print("| Name: " .. v.Id .. "\n| ['X'] Position: " .. v.Position.X .. "\t| ['Y'] Position: " .. v.Position.Y .. "\t| ['Z'] Position: " .. v.Position.Z)
	local part = replicatedStorage:WaitForChild("Shared Instances")["Placement Storage"][v.Id]:Clone()
	part.Parent = workspace.Plots["1"].Building
	part.Position = Vector3.new(v.Position.X, v.Position.Y, v.Position.Z)
end
end)

Note: The v.Id is basically you’ll need a Reference to the part in the server.

7 Likes

Here’s a small example of saving data using datastore.

PartsArray = {true} -- the data you want to store

DataStoreService = game:GetService("DataStoreService")

local UserId = 2446384 -- the userid of the user you want to save to

local PartStorage = DataStoreService:GetDataStore("PartSaves", UserId)

PartStorage:SetAsync("PartSave",PartsArray)

print(PartStorage:GetAsync("PartSave")[1]) --> true
3 Likes

GetAsync is not a valid member of DataStoreService, what do i do?

I also got this error: ServerScriptService.LoadAndSave:2: bad argument #1 to ‘pairs’ (table expected, got no value)

1 Like

Make sure you’re using GetAsync on the DataStore itself, not on DataStoreService!

4 Likes

i did that and i got this error: ServerScriptService.LoadAndSave:2: bad argument #1 to ‘pairs’ (table expected, got no value)

1 Like

Let me see your code, that’s the only way I can properly diagnose the issue

1 Like
replicatedStorage = game:GetService("ReplicatedStorage")

LoadEvent = replicatedStorage:WaitForChild("LoadEvent")
SaveEvent = replicatedStorage:WaitForChild("SaveEvent")

LoadEvent.OnServerEvent:Connect(function(player, name, pX, pY, pZ)
	dataStore = game:GetService("DataStoreService"):GetDataStore(player.UserId)
	local oldData = {}
	table.insert(oldData, {Id = name, Position = {X = pX, Y = pY, Z = pZ}})
	if dataStore:GetAsync(player.UserId) ~= nil then
		for i,v in pairs(dataStore:GetAsync(player.UserId)) do
			table.insert(oldData, v)
		end
		dataStore:SetAsync(player.UserId, oldData)
	else
		dataStore:SetAsync(player.UserId, oldData)
	end
end)

SaveEvent.OnServerEvent:Connect(function(plr)
	dataStore = game:GetService("DataStoreService"):GetDataStore(plr.UserId)
	for i,v in pairs(dataStore:GetAsync(plr.UserId)) do
		print("| Name: " .. v.Id .. "\n| ['X'] Position: " .. v.Position.X .. "\t| ['Y'] Position: " .. v.Position.Y .. "\t| ['Z'] Position: " .. v.Position.Z)
		local part = replicatedStorage:WaitForChild("Shared Instances")["Placement Storage"][v.Id]:Clone()
		part.Parent = workspace.Plots["1"].Building
		part.Position = Vector3.new(v.Position.X, v.Position.Y, v.Position.Z)
	end
end)
2 Likes

You’re not doing a sanity check for GetAsync - you need to make sure that GetAsync doesn’t return nil (it returns nil if there’s no data)

You should also be wrapping your datastore calls in pcalls, but it isn’t necessary

1 Like

I’d save the CFrames of each part that you want to save and store it as a table in a datastore. Position is a Vector3 and doesn’t account for rotation. Best of luck!

2 Likes

Expanding on this, because you want to save parts in general you should save all of the properties that you would need.

Some vital properties that might be important to save (in general) are:

  • CFrame (position + orientation in one)
  • Color (you need to serialize this)
  • Size
  • Material (I believe you need to serialize this)

Still depends on your use case though

2 Likes

im working on a classic styled building games, there are many diffirent parts too. some also do stuff, like move using a body thrust, or be clickable. Similar to an older game that sharky99 made

1 Like

(i know i am little late, but i am going to post anyways),

Datastore

As Far as Datastores go there are pretty fun(at least for me) and there are alot of resources(below) to help you understand them, here’s a quick example nonetheless:

(Saving)

---this example is using update async( there are 3 other ones to use)
local DataStoreService = game:GetService("DataStoreService")
local MyStore =  DataStoreService:GetDataStore("Test")
local Key = player --Our Scope, the key,  we want to save our data under

 MyStore:UpdateAsync(Key, function(prev)----unlike other Datstore functions(like SetAsync), this one returns the previously  saved value to our key
if prev == nil then
  prev = {}
end
 local RandomNumber = math.random(1,50)
table.insert(prev, #prev + 1, RandomNumber)
  return  prev --- Update Async, essentially is looking for a returned value, to, well update our previous value to! (in this case we "overwrite" our table with a newer version of our table
end

(loading data)

local DataStoreService = game:GetService("DataStoreService")
local MyStore =  DataStoreService:GetDataStore("Test")
local Key = player --Our Scope, the key,  we want to save our data under
--This time we are going to use Get Async to get our data
local success, result =  pcall function() --- pcalls are pretty handy when working with datastore, they "don't" error and just return wether the  code inside of it worked and the result
local data = MyStore:GetAsync(key) --- All we need to put here is our player (key) to get the table saved above
end


Saving Parts

There are a few ways of saving parts, some people choose to save parts( positions of parts not actual parts) under a table and use non-string indices( which i find to be one of the better ways) . Note that parts can not be directly saved to a Datastore, and therefor a string or number indicating which part needs to cloned needs to be passed. Keep in mind that if you were to save many parts, there is a chance you could run into the Datastore limit (one of which is 260,000 characters per key) , which is why you should only save necessary things. Saving the parts’ position is an example of a necessary thing, however something like the size might not(depending on your game). So if i were making a building game for example, to save parts i would only save the string/number associated with an object i could clone, from let’s say the replicated storage, and i would only save the position and the color of the part

(Recommend (more) Practice)

local PartToSave = {
[1] = "ACoolPart"
[2] = 0,0,0 ----As a Note, you cannot save color3s as it is not a valid in UTF-8 terms
[3] = {50,-70,100}
} 

(Not recommend Practice)

local PartToSave = {
["Name"] = "ACoolPart"
["Color"] = 0,0,0 --String index
["Position"] =  {50,-70,100}
} 

Compression Techniques/Safe Saving?

Safe saving and being compact is important when creating building systems, for compression you could go and even use numbers to represent different variations of parts(recommend), if you wanted to you could use compression techniques (in resources), or you could just use some other techniques like the theoretical one mentioned here:

https://devforum.roblox.com/t/using-multiple-data-stores-for-more-capacity/356256

(i have not personally used this one but it might be good to check it out if you want to)

Resources

Stravant's BitBuffer Module - Compact Storage Of Data [Tutorial]

Details on DataStoreService for Advanced Developers

Data Stores | Documentation - Roblox Creator Hub

GlobalDataStore | Documentation - Roblox Creator Hub

Lua Globals | Documentation - Roblox Creator Hub – pcalls are lua globals

Stop using SetAsync() to save player data – this is a good read
Videos:

https://www.youtube.com/watch?v=DkYupSBUpes

https://www.youtube.com/watch?v=I4A9qosS9xo

https://www.youtube.com/watch?v=Nfm-CtRUNqk – a little old but should still work!

Other:
How to save Dictionary using DataStore2? - #4 by Operatik

Handling sandbox tycoon data —Have not tested

Should i be concerned with the datastore data limit? - #9 by Vivid_Void

Creating A Furniture Placement System --At the bottom Ego Moose does a pretty good job of explaining saving placement system data (its brief but still)

20 Likes

Hay, this is probably late, but this might help as well!

3 Likes