How to save parts, and the idea of Serialization

Ended up with this, it does work well but maybe you have an idea on further optimization.

local tableToSave = {
	Dirt = {},
	Stone = {},
	HardenedStone = {}
}

SaveTriggerEvent.OnServerEvent:Connect(function()
	
	for _,v in pairs(BlocksFolder.Dirt:GetChildren()) do
		table.insert(tableToSave.Dirt, v.Position)	
	end
	
	for _,v in pairs(BlocksFolder.Stone:GetChildren()) do
		table.insert(tableToSave.Stone, v.Position)	
	end
	
	for _,v in pairs(BlocksFolder.HardenedStone:GetChildren()) do
		table.insert(tableToSave.HardenedStone, v.Position)	
	end
end)

LoadTriggerEvent.OnServerEvent:Connect(function()
	
	for _,v in pairs(tableToSave.Dirt) do
		DrawBlock("Dirt",BlocksFolder.Dirt,Vector3.new(v.x,v.y,v.z))
	end    
	for _,v in pairs(tableToSave.Stone) do
		DrawBlock("Stone",BlocksFolder.Stone,Vector3.new(v.x,v.y,v.z))
	end
	for _,v in pairs(tableToSave.HardenedStone) do
    DrawBlock("HardenedStone",BlocksFolder.HardenedStone,Vector3.new(v.x,v.y,v.z))
	end
end)
1 Like

Ik this is a very late question but i was wondering why Unions dont show up when i load them in? i wrote a table for them and everything.

UnionOperation = {"Name", "Position", "Size", "Transparency", "BrickColor", "CanCollide", "CFrame", "Anchored" , "CastShadow" , "CollisionGroupId" , "Material"},

it still displays the output but the union is invisible

1 Like

Looking great. Doesn’t seem to lag when loading nor saving. If you’re planning to make the world bigger there might be a need to optimize it size-wise, else you’re good. As for loading, if you wanna make that faster, perhaps just replace the generic for loop with a renderstep, maybe doing everything under a single for loop is enough to make it slightly faster.

@KreatorKols Unions are a hard thing to save unfortunately! The problem is if you wanted to save a union, you technically need to save the parts that make up the union (the negative parts or whatever), and currently there is no way to get those. So unless the unions are common object that you have presets for like in Czlopek’s case, I don’t think you can do much besides keeping a table for each union specifying details on the parts that make up the union, information used to save and load those parts back, and do the necessary operations (the :UnionAsync() method) to make the union again.

4 Likes

(This is my first post on the forms so sorry if its bad) I just wanted to start by saying this is really well worded! I do have a question though. I have been trying to serialize a RodConstraint and its attachments, I have gotten the attachments to work but I’ve gotten stuck on RodConstraints, specifically the “Attachment0” and “Attachment1” part of RodConstraints. Any ideas on how serialize it?

2 Likes

for me roblox should add byte arrays to roblox

1 Like

Not a terrible idea, you can create something like that on your own. One does already exist though, check this out

1 Like

Hey! Sorry for the late answer (and what are the odds, as soon as I noticed your reply you made a topic about the question).

I didn’t actually cover this, and I should to be honest! There are many ways to take care of properties set to instances, but I think the easiest way would be to include a separate dictionary inside of the saved table, where each key is an id, set to a serialized instance.

When you save an object which has a property set to an instance, say Attachment0, you will generate a random id, in the serialized form set the property to that id.
This id will serve as a lookup in the separate dictionary I mentioned. In this dictionary you will save the serialized form of the instance the property was set to. I think you know where this is going.

When deserializing, if you come across Attachment0, you will grab its stored id, look for the part corresponding to its id inside of that dictionary, deserialize the part, and set Attachment0 to the part!

1 Like

Alright, I have to admit I’m a little confused but hey its the only way we learn! My main question is how would I identify the id? I guess I don’t really understand the whole id thing.

1 Like

The serialized property contains the id. The id is randomly generated when serializing, and the instance the property was set to will be serialized and inserted into a dictionary (that is saved too) under a key, where that key is the randomly generated id.

When deserializing, when you deserialize the property, you grab the saved id, go to the saved table containing the ids and corresponding instances, and grab the instance corresponding to the id you have, and deserialize it, and set the property to the instance!

This is the processs in short:
Serialization:

  • Generate ID
  • Set serialized property to the ID
  • Serialize instance the property is set to
  • Inside of a saved dictionary containing ids, insert the serialized part under the generated id

Deserialization:

  • Grab the ID from saved property
  • Index the saved dictionary containing the IDs to get the serialized part
  • Deserialize
  • Set the property to the deserialized part

And one thing, if the instance the property is set to is a child of some object (or maybe the object indexing the property itself), it might be serialized twice, and two copies might exist when you deserialize. To circumvent this, you might need to add some checks, or maybe change how the ID is saved.

1 Like

Could you give a code example? I really want to learn how to do this, but I simply don’t think I’m going to be able to do it without some help. I also understand writing a code sample is typically more time consuming then just a normal reply so don’t feel any pressure to do so.

Hi! So I have a building game and I am making a save/load feature. I have followed your instructions and everything is working, expect whenever you make for example a small house and save it, make some changes and save it again, all the blocks that were already saved are saved again. So you will have two houses in one because all the blocks are saved twice. I tried to fix this by going through the database with a for loop and check all the positions of the already saved blocks. Whenever the position of the new block is already saved in the database, it shouldn’t save. But it does. I have been trying to fix this for the last two days without success. Maybe you can help me?

This is the code of the InitProps function (everything else is the same as in the topic):

function InitProps(objects)
	local DS = game:GetService("DataStoreService"):GetDataStore("Slot")
	local ency = DS:GetAsync(tostring(game.Players:FindFirstChildOfClass("Player").UserId))

	local decoded = HttpService:JSONDecode(ency)

	local tableToSave = {}

	for _, obj in pairs(objects) do
		local class = obj.ClassName
		local t = tableToSave[class]

		local alreadyExists = false 

		if not(t) then
			tableToSave[class] = {}
			t = tableToSave[class]
		end

		for _, others in pairs(decoded) do
			for prop, value in pairs(others) do
				for data,values in pairs(value) do
					if data == "Position" then
						local posx = obj.Position.X
						local posy = obj.Position.Y
						local posz = obj.Position.Z
						
						if posx ~= values.X or posy ~= values.Y or posz ~= values.Z then
							
						else
							print("NO")
							alreadyExists = true
						end
					end
				end
			end
		end
		
		print(alreadyExists)
		
		if not alreadyExists then
			local add = {}

			for _, Prop in pairs(Properties[obj.ClassName]) do
				add[Prop] = Serialize(obj[Prop])
			end

			local children = obj:GetChildren()

			if #children > 0 then
				add["Children"] = InitProps(children)
			end
			table.insert(t, add)
		end
	end
	return tableToSave
end
1 Like

Why don’t you instead of saving what was newly added, re-save the entire house again. Or are you looking for efficiency? In that case, perhaps a simpler solution is having an array of anything added after the last save. When saving again add it to the previous save, and empty this array for it to be refilled again.

Why don’t you instead of saving what was newly added, re-save the entire house again.

I tried to do this first, but I found on the Forum that you can’t delete any data from the Datastore for some reason, so I thought it was impossible.

1 Like

Well you don’t have to delete it, if you save the serialized data under the same it will overwrite the old data.

Could you please show me an example of what you mean?

A little overthinking on saving EnumItem, just save EnumItem.Value and it will return the number and it will still work. Some parts could have been done better, but we appreciate your contribution to the community anyway.

1 Like

I’m getting the error invalid argument #1 to 'pairs' (table expected, got nil)
my code is

local ourFolder = workspace.Places[player.Name]:GetChildren()

    for _, v in pairs(ourFolder) do
        print("got item")
    end

    local result, Err = pcall(function()
        DS:SetAsync(player.UserId, Save.Encrypt(ourFolder))
    end)

it does print “got item”

Weirdly enough though the Value does actually change overtime it seems! As it did here

image

3 Likes

What are the odds of my words turned against me. :joy:

Well, glad you pointed that out. I see why we should use strings instead of a number. I appreciate it.

3 Likes

I’m currently working on a module called Instance Service. Which can save every type of instance using DataStore2. Fully configurable.
Everything works well right now. It’ll come out 2 weeks later.

I’d wanted to say that here because many people actually really wants to learn how to save parts.

2 Likes