How to save parts, and the idea of Serialization

I know that still not answering my question.

Oh I think I see what you mean now that I read it again. The reason why strings are better is because it is required to send the data to a custom datastore. If you’re using the default roblox data store then it won’t be a problem since this is called automatically, but if you’re using mongoose or google spreadsheets or something (I wouldn’t recommend using spreadsheets as a datastore) then you’re going to need this in string format to send it over properly. Was this what you were asking?

Really sorry for the long wait and the late respone.

tl;dr, this is a redundancy of my part, you don’t actually need to :JSONEncode() the dictionary, roblox does it anyways internally, simple dictionaries that don’t contain userdata (Vector3s, CFrames, instance ect.) can be saved ay okay. People usually get a UTF-8 error when they try to save a userdata, and end up :JSONEncode()ing the thing which won’t work out, it just ends up replacing the userdata with null, and loading the data back will just cause trouble. I will change this! Thanks.

@sjr04 @bigcrazycarboy

2 Likes

This is amazing, I don’t see myself as being that good at scripting, but you explained it very well.

I was able to modify it to save when the player leaves and load when they press a gui button.

Thank you!

1 Like

[SOLVED!]

I am getting an error. In the function InitProps(objects).

The error is: invalid argument #1 to ‘pairs’ (table expected, got nil).

I am using datastore2 instead and Im hoping I did it right. Would be able to help if I give more info?

solution: starmaq: Like you said, it’s calling Serialize() sometimes but ends up failing at a certain point. This is because at some point the loop will loop through an obj that doesn’t have the properties for his class inside of the properties table. Check which object that is, and add the properties of it that you wanna save in the form of strings! Thanks for the video as well, helped me understand your problem.

Can you please give which line gave the errors? Because there are 2 possibilities. The problem could also just be that you didn’t pass a table to Encode which calls InitProp with that table, in which case revise your code first please.

Absolutely. Here is the playerData script I have that contains the reference to the module script serialize:

--required vars
local datastore2 = require(game.ServerScriptService:WaitForChild("DataStore2")) --1936396537 game.ServerScriptService:WaitForChild("DataStore2")
local serialize = require(game.ServerScriptService:WaitForChild("serialize"))

--default vars
local players = game:GetService("Players")
local defaultFunds = 1000

--DONT FORGET:: COMBINE!
datastore2.Combine("playerData", "Funds", "parts")

game.Players.PlayerAdded:Connect(function(player)
	--datastore vars
	local fundsData = datastore2("Funds", player)
	local partsData = datastore2("parts", player)
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local funds = Instance.new("IntValue")
	funds.Name = "Funds"
	funds.Value = fundsData:Get(defaultFunds)
	funds.Parent = leaderstats
	
	--callbacks
	fundsData:OnUpdate(function(newValueFunds)
		funds.Value = newValueFunds
		local fundstxt = player:WaitForChild("PlayerGui"):WaitForChild("ScreenGui").playerFunds
		fundstxt.Text = player:WaitForChild("leaderstats").Funds.Value
	end)
	
	--add a parts folder to the player in case of store purchases
	local partsFolder = Instance.new("Folder")
	partsFolder.Parent = player
	partsFolder.Name = "Parts"
	----LOADING PARTS FOR EACH PLAYER
	--might put this somewhere else, stand by
	
	
	--SAVING PARTS FOR EACH PLAYER
	--current saving action is done by clicking a basepart(being tested)
	workspace.workArea.Parts.ChildAdded:Connect(function()
		wait(1)
		for _, area in pairs(game.Workspace:GetChildren()) do
			if area.Name == "workArea" then
				if area.ownerShip.Value == player.Name then
					local parts = area:FindFirstChild("Parts")
					if partsData ~= nil then
						local pArray = serialize.Encode(area.Parts:GetChildren())
						partsData:Set(pArray)
					end
				end
			end
		end
	end)
end)

game.Players.PlayerRemoving:Connect(function(player)
--NOTHING TO PUT HERE JUST YET
end)
```lua


AND in the serialize module this is the line with the error:




```lua
function InitProps(objects)
	local tableToSave = {}
	for _, obj in pairs(objects) do
		local class = obj.ClassName
		local t = tableToSave[class]
		if not(t) then
			tableToSave[class] = {}
			t = tableToSave[class]
		end
		local add = {}
		*for _, prop in pairs(properties[obj.ClassName]) do*  --errors here
			add[prop] = Serialize(obj[prop])
		end
		local children = obj:GetChildren()
		if #children > 0 then
			add["Children"] = InitProps(children)
		end
		table.insert(t, add)
	end
	print("00000000")
	return tableToSave
end
```lua





NOTE: I have tried printing tables that are passed and messing around with the datastore2:Set() parameters for 2 days so far. Each one of them give the error. 
What is interesting is, I can print properties([obj.ClassName]) and it actually prints the objects and the properties I have assigned. The error comes right after that. 

So, the module.Serialize function works! I am returning (r) just fine for the properties. Thats why I am confused it errors lol. 
So, as long as I have that error, since it is not a warning, I cannot go further yet. 
I am betting it is something to do with the passing of the arguments as you mentioned.
I am not assigning a default to the partsData, but I am assigning a default to the fundsData. 
I am testing that now. I have read that could cause issues by not having a defualt. 

Thats where I am :P Thank you for the help and no rush ok? I appreciate all the time for sure!
1 Like

I am posting a video of it also because videos help me A LOT! So, hopefully we can see something :slight_smile:
Thanks again!

1 Like

Like you said, it’s calling Serialize() sometimes but ends up failing at a certain point. This is because at some point the loop will loop through an obj that doesn’t have the properties for his class inside of the properties table. Check which object that is, and add the properties of it that you wanna save in the form of strings! Thanks for the video as well, helped me understand your problem.

1 Like

You are more than welcome and Im glad you see the same as I presumed :slight_smile: I will add at least one property of each child into the properties array and see if it works by at least looping through and finishing.
Will update soon and mark your answer a solution if all good :slight_smile: Im assuming the same, that everything will work once I do that. Wish me luck!

SOLVED :slight_smile: I have the properties set like this if say the part has these classes as children.
lua
local properties = {
Part = {“Name”, “BrickColor”},
ClickDetector = {“Name”},
IntValue = {“Name”, “Value”},
StringValue = {“Name”, “Value”},
Script = {“Name”}
}

image_2020-10-01_170004

Thank you for the insight and help again! This is a great effort and appreciated. Do you want anything for the time you put into making this?

Now, I just have to figure out how to remove items and that’ll be easier I hope lol. Good to go and hope this helps anyone who might need to change the properties for their situation!

1 Like

I got the deleting thing down :slight_smile: In datastore2, if you use datastore2:Set(), it automatically resets the cache in that datastore and sets new values :). So, all is good so far exept one thing lol.
That one thing is, I have a script inside the children.
I know that I could simply put the script in the workspace or somewhere and simply reference from there and use remote events etc. BUT…I would like to know if, using the serialization/deserialization here, how to bring back the contents of the script as a child.
Currently, when the script comes back, its empty :stuck_out_tongue: Everything else saves and loads perfectly except that and surface types[easy fix].

Do you know how to load back script contents or say save them too?
Thank you :slight_smile: This might be useful for localscripts. Mine is a serverscript so Ill have to test local out but figured Id find out if there is a way within the module to do so :slight_smile:

Hope to hear from you soon!

Sadly you can’t save script’s content. Scripts have a .Source property which contains their content, but it’s not available during runtime, so other scripts can’t access it. Instead of remaking the script, you can put an original version of it somewhere in server storage, and when you load, get the name of the saved script, and copy it from server storage and parent it where you need to.

2 Likes

No worries, I ended up doing a for loop and grabbing it from server storage :slight_smile: THANK YOU!

1 Like

I have a good question :slight_smile: What if I need to save the CFrame that are relative to another part that doesn’t stay in the same place every time I join?
In serialize, there is a “CFrame” class, so how can I use that to serialize and deserialize it relative to another part?

specifially, what would I do to these codes?

serialize part:
r = {pos = Serialize(prop.Position), rX = Serialize(prop.rightVector), rY = Serialize(prop.upVector), rZ = Serialize(-prop.lookVector)}

deserialize part:
r = CFrame.fromMatrix(Deserialize(“Position”, value.pos), Deserialize(“Position”, value.rX), Deserialize(“Position”, value.rY), Deserialize(“Position”, value.rZ))

Hey, could you expand on this topic?
I’m trying to make a world-saving system, got random generation set up, every part is stored in a separate folder based on type, and its cloned from the preset part with a unique name.
The problem is I still don’t know how to save them efficiently, your module works fine but the number of blocks will quickly fill the memory and I have no idea how to segregate them by name.

Its a 2d mining game of sorts.

1 Like

Hey! So, the serializing I’m doing in the article will probably not be good in your case, since as you already said, you all the parts in seperate folder, which will help in the long run and in our scenario, and you have a preset part for each type of blocks. If I were to quote myself:

Note that, for games that have the same type of objects being used again and again (e.g. a building game, where you just have 3 (or more) types of building parts, like brick, dirt and plastic) you can simply save a table containing table where the key is each object’s name, and that table will hold a long list of Vector3 s that indicate where each block of that certain type is placed. And when unloading, loop through the table, and each time just :Clone() the original object of the current type (assuming it’s stored in ServerStorage or ReplicatedStorage ) and position it at the current position. This is better than having to save properties and extra info we don’t need. Of course this depends on the structure of your game, you might need to store additional info such as color maybe.

A better serializing technique would be like this, when saving, have a table, where each key is the type of the block, and each key holds a table with a lot of serialized positions. When you load the data back, loop through each key, for each key you’re gonna be looping, copying and placing the corresponding preset part for each position. This is a good strategy.

local toSave = {
    Dirt = {loop through all dirt blocks, put the position of each one here},
    Stone = {loop through all dirt stone put the position of each one here},
    ...
}

As for the positions, this is a 2D game, all you need to save is an X and a Y, which could simply be a string containing two numbers seperated by a comma or something else. "x,y".

So, efficiency does matter in your game. When you save something, that something gets JSONEncoded, so the length of the generated JSON string is the limit. Recently, the limit was increased to 1 MB, a character is 1 byte, meaning you can save approxiametly a million character long string. I doubt the game has more than 10 tousand blocks, and for each block you’re gonna need to save a 6 or 7 characters long string, which is its position, you have the comma, and I assume the Y and X directions will go hundreds of blocks in that direction, that’s 7. 10000 * 7, that’s 70000, we’re not even close to the limit. Of course there is more overhead like the [ and , found in the JSON string, let’s just say another extra 20000. Again, these are just my expectations, you can count and see for yourself, if you find issues with hitting the limit, then you would need to do some form of serialization.

3 Likes

So rewriting the module is my only hope.

At first, I thought that was causing stutter while loading blocks.

I guess learning a bit about tables won’t hurt because i don’t know what should i do with this.

1 Like

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