Trying to implement saving into Zblock's Placement System but I cant seem to do it

Hello, I want to implement a saving system into Zblock’s placement system.
So I thought of Serializing the objects so they are stored as Dictionaries, with the key being the name of the model and the value being the CFrame of the model.

But when I try saving it onto a Datastore I get this error:
[20:31:56.319 - 104: Cannot store Dictionary in data store. Data stores can only accept valid UTF-8 characters.

But I have UTF-8 characters.

Serialization Function:
function M.Serialize()
local serial = {}

local ItemHolder = workspace.Plots.Plot.ItemHolder

for i,v in pairs(ItemHolder:GetChildren()) do
	serial[v.Name] = v.PrimaryPart.CFrame
end
	
return serial
end

Deserialization Function:
function M.deSerialize(canvasPart, data)
data = data or {}

for i,v in pairs(data) do
	local CF = v
	local modelName = i
	
	if game.ReplicatedStorage.Models:FindFirstChild(modelName) then
		print(CF)
		local newModel = game.ReplicatedStorage.Models:FindFirstChild(modelName):Clone()
		newModel.Parent = canvasPart.ItemHolder
		newModel:SetPrimaryPartCFrame(CFrame.new(CF.X, CF.Y, CF.Z))
	end
    end
end

**Saving and Loading Script: **
local DSService = game:GetService(“DataStoreService”)
local saveStore = DSService:GetDataStore(‘saveStore_002’)
local SavingModule = require(game.ReplicatedStorage.Modules.SavingModule)
local HTTP = game:GetService(“HttpService”)

game.Players.PlayerAdded:Connect(function(Player)
	local Scope = 'id-'..Player.userId
	
	local Data = SavingModule.Serialize()
	
	local GetSaved = saveStore:GetAsync(Scope)
	if GetSaved then
		print(GetSaved[1])
		SavingModule.deSerialize(GetSaved[1], workspace.Plots.Plot)
	else
		print(Data["Chair"])
		saveStore:SetAsync(Scope, Data)
	end
end)

I would really appreciate some help.

I do not believe that Roblox Datastores allow the storage of instances (including CFrames) in Datastores, you can only take necessary information out of the instance so that you can recreate the object from the saved data once the player rejoins.

Yeah but how would I save the name, the position and the rotation of the object if I cant even save a CFrame value?

EDIT: Added ‘the name’.

i think u can make from CFrame into a number by toumber(CFrame) and when loging using CFrame.new(THE_DATA_U_RECEIVE)

You will have to save all the values individually in a table. For example:

nameOfCFrame = {
    x = CFrame.x,
    y = CFrame.y,
    z = CFrame.z,
-- all other values in a CFrame
}
2 Likes

How would I link those cframe values to the Object?

It does not work.
It returns the error:

[20:51:35.815 - ReplicatedStorage.Modules.SavingModule:18: bad argument #1 (table expected, got Object)

what code u used? 30charsssssssssssssssss

1 Like

What do you mean with what code I used?

what code u put to save? the script?

You actually can save instances with datastore. However this is deemed very unnecessary since datastores has limits. So that is why it is good practice to only save the positions (…for instance).

I used
saveStore:SetAsync(Scope, Data)

Whole script:

local DSService = game:GetService("DataStoreService")
local saveStore = DSService:GetDataStore('saveStore_003')
local SavingModule = require(game.ReplicatedStorage.Modules.SavingModule)
local HTTP = game:GetService("HttpService")

game.Players.PlayerAdded:Connect(function(Player)
	local Scope = 'id-'..Player.userId
	
	local Data = SavingModule.Serialize()
	
	local GetSaved = saveStore:GetAsync(Scope)
	if GetSaved then
		print(GetSaved[1])
		SavingModule.deSerialize(workspace.Plots.Plot, GetSaved[1])
	else
		saveStore:SetAsync(Scope, Data)
	end
end)

game.Players.PlayerRemoving:Connect(function(Player)
	local Scope = 'id-'..Player.userId
	
	local Data = SavingModule.Serialize()
	print(Data)
	saveStore:SetAsync(Scope, Data)
	
end)

i don’t see where u save the data, and i don’t what the things in module does

A similar thread has been posted on the forum. I recommend checking out datastore 2, it is easy to comprehend and the module itself is very versatile. And since you’re probably making a building-system you’d need a lot of compression in your data so again, I refer back to the datastore 2 system, give it a try.

What I caught you doing in your serialization function is that you stored your keys as string indexes which is BAD PRACTICE!

Yours is the equivalency of this:
mytable = {
[“randomString”] = blah
}

Instead you should actually be doing this:

myTable = {
[1] = blah
}

So,

I tried another approach. I basically have the same system but as you mentioned I use Datastore 2.

But I changed up the Deserialization and Serialization Function:

local M = {}


function encode_cframe(cf)
    return string.format(string.rep("%f ",12), cf:components())
end

function decode_cframe(cf)
    local tab = {}
    for s in string.gmatch(cf, "%d+") do
        table.insert(tab,tonumber(cf))
    end
    return CFrame.new(unpack(tab))
end

function M.Serialize()
	local serial = {}
	
	local ItemHolder = workspace.Plots.Plot.ItemHolder
	
	for i,v in pairs(ItemHolder:GetChildren()) do
		serial[v.Name] = encode_cframe(v.PrimaryPart.CFrame)
	end
	return serial
end

function M.deSerialize(canvasPart, data)
	data = data or {}
	for i,v in pairs(data) do
		local CF = v
		local modelName = i
		
		print(modelName)
		
		if game.ReplicatedStorage.Models:FindFirstChild(modelName) then
			local newModel = game.ReplicatedStorage.Models:FindFirstChild(modelName):Clone()
			newModel.Parent = canvasPart.ItemHolder
			newModel:SetPrimaryPartCFrame(CF)
		end
	end
end

return M

Problem is that I can not save CFrame’s so I tried encoding it into a number and then when I want to load it I would decode it. But when I try to decode it, it returns 0,0,0 etc

So I removed the Decoding thing into deSerialize and because of the way I encoded it the CFrame is normal but with no commas so it’s not a real CFrame

So I try playing it but it returns this error because it’s not a real CFrame:
[21:40:58.770 - Unable to cast string to CoordinateFrame

How would I make a better function to encode and decode cframe to convert a cframe into a string and a string into a cframe?

Edit: Spelling Errors

As @NicholasY4815 said, you need to save each individual number.

nameOfCFrame = {
   x = CFrame.x,
   y = CFrame.y,
   z = CFrame.z,
   -- Any other values
}

Yeah but how do I link those CFrame values to an Object?

And what do I do when I need to load it?

Not the case. You can’t save unserialised instances to DataStores because the service does not support userdata in the first place. It’s only meant to store pure data (strings, numbers and tables) which are coerced into a JSON format under the hood. Roblox datatypes and classes cannot be converted back and forth from a JSON format and even if they could, it could result in unexpected behaviour (e.g. lost metatable).

Nothing to say they can’t just implement serialisation to achieve what Save/LoadInstance did but that’d eat up the space you’re given, so its not worth the jump especially when you can “save instances” in just a few letters and numbers.

When I JSON encode the dictionary and I print it for debugging it returns:
{“Table”:22.38999176025390625,“Chair”:5.1999912261962890625}

I did CFrame:GetComponents()

And it seems that I do not get the full CFrame.

And If I remove :GetComponents() then it returns as null:
{“Table”:null,“Chair”:null}

That’s possible because GetComponents returns the values that make up the CFrame and this is a legal format for DataStores. You aren’t working in terms of the CFrame, you’re working in terms of the values that make it up.

As for why you aren’t getting all the components, values may be getting dropped. Its best to print out the entire return of GetComponents, iterative or not, to check if the formats you’re attempting to work with are legal for JSON.

1 Like