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 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.
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).
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)
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
}
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?
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.
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.