Cant store my dictionary in the datastore

I have a datastore that takes specific details about parts in a model and saves them as a dictionary, but to my disappointment I could not store what I had due to an error: cannot store dictionary in datastore, datastores can only accept valid UTF-8 characters.

I read a post with the same problem and they said to “serialize” the information as its appearently too big. but I don’t know how I would do that considering I only used minimal information.

here is the script:

-------------------------------------------------------dats stores--
--------------------------------------------------------------------
local datastore = game:GetService("DataStoreService")
local mydata = datastore:GetDataStore("mydata")



game.Players.PlayerAdded:Connect(function(plr)
	local folder = game.ServerScriptService.DataCore:WaitForChild(plr.Name)
	local searchtimeout = 50
	local buildspace = folder.AssignedBuildSpace
	
	
	repeat
		wait(0.1)
		searchtimeout = searchtimeout - 1
	until
	buildspace.Value ~= nil or searchtimeout <= 0
	
if not buildspace then
	print("no buildspace found for loading")
return
end
	
	local data
	local success,fail = pcall(function()
	data = mydata:GetAsync(plr.UserId)
	end)
	if success and data then
	--load data------
for _, p in next, data do
	wait()
	if type(p) == 'table' then
local currentname = p["name"]
	local foundpart = game.ReplicatedStorage.BuildingParts:FindFirstChild(currentname)
	if foundpart then
		local copy = foundpart:Clone()
		copy.Parent = buildspace.Value.Parent.Parent.PlayerBuilding
		copy.Size = p["size"]
		copy.CFrame = buildspace.Value.CFrame:ToWorldSpace(p["cfr"]) 
		copy.BrickColor = p["col"]
	end
		
	end
end
	-----------------
	
	end
	end)




game.Players.PlayerRemoving:Connect(function(plr)
	print("saving...")
	local folder = game.ServerScriptService.DataCore:WaitForChild(plr.Name)
	local data
	
	--model saving code--
	local buildspace = folder.AssignedBuildSpace
	if not buildspace.Value then
		print("no space found for saving")
		return
	end
	local parts = buildspace.Value.Parent.Parent.PlayerBuilding:GetChildren()
	local building = {}
local addindex = 1
for i = 1, #parts do
	print("loop ran")
	if parts[i]:IsA("BasePart") then
		print("confirmed basepart")
		local currentsurface = buildspace.Value.CFrame:ToObjectSpace(parts[i].CFrame)
		print("currentspace is buildspace")
		local partinfo = {
			name = parts[i].Name;
			cfr = currentsurface;
			size = parts[i].Size;
			col = parts[i].BrickColor
		}
		print("set part properties")
		local indexstring = "partinfo"..addindex
		building[indexstring] = partinfo
		addindex = addindex + 1
		print("added info to building table")
	end
	
end
	---------------------
	
	--data to store--
	data = building
	-----------------
	--local success, fail = pcall(function()
	mydata:SetAsync(plr.UserId,data)
	--end)
	--if success then
		print("save successful")
	--end
end)
---------------------------------------------------------------
---------------------------------------------------------------

a few notes:

SetASync at the bottom has its Pcall turned into a comment in order to see the error come up.

the building dictionary is the model, the partinfo dictionaries are the parts inside the model that are being saved.

partinfo dictionaries only contain basic info such as CFrame, color, name, etc.

why is this happening? and what do I do to fix this?

The name field in the table could probably be the issue, because it could have invalid UTF-8 characters. Try excluding it from the save table and see what results you get.

It’s either that, or the CFrame and BrickColor need to be serialised, probably

Im not sure how the name could cause problems as it only contains upper and lowercase letters. and I removed the brickcolor properties and the problem persists.

can datastores handle uppercase letters?

Uppercase characters are valid UTF-8 characters, but there could be something that makes it invalid, who knows :man_shrugging: . The Vector3 could also be an issue that I forgot to mention.

You’d need to test all of the data separately, like the name and the Vector3, CFrame and BrickColor. If it’s not the name that causes it, the CFrame etc. need to be serialized

what about CFrame? the value “currentsurface” is a CFrame instead of a vector3.

You should only include the name and the CFrame, vector, etc. each separately. You’d take out the CFrame, vector and BrickColor and leave the name behind, if the problem persists, the name field is the cause. If it doesn’t persist, repeat for the CFrame, vector and BrickColor.

If its the case where name is the cause, you can give the parts a number to identify a part that should be added, like an ID. If either the CFrame, vector and BrickColor is the cause, they have to be serialised

so it looks like both CFrame and Size are the cause, only the name is accepted in the datastore, how would I make these properties more palatable for the datastore?

You’d have to store all of the components in tables, so you wouldn’t save the CFrame and Vector class itself, but rather a table that stores their components. Upon joining, you’ll need to form the CFrame / Vector with the saved components:

These functions can help:

function serialiseCFrame(cf)
    return {cf:components()}
end

function serialiseVector(v)
    return {v.x, v.y, v.z}
end

function deserialiseCFrame(tbl)
    return CFrame.new(unpack(tbl))
end

function deserialiseVector(tbl)
    return Vector3.new(unpack(tbl))
end

Although, for CFrames, you’d want to store the least amount of data as CFrames can hold a lot of data relatively, since a CFrame matrix has 12 numbers. You can use ToEulerAngles and save the returned angles in the table and then apply to the parts upon joining.

You don’t need to take the above into account if your parts don’t have an applied rotation

4 Likes

sounds good, so basically I include these functions inside my datastore script,
and when I need to convert values I just use them when needed?
also, in terms of space would it be more or less data to save position and orientation separately instead of CFrame?

Precisely.

Saving position and orientation would use less data in relation to a whole CFrame, as you’re only saving 6 values rather than double that amount. CFrame:ToOrientation can help you