I am working on a plot saving/loading system for when the player leaves and rejoins but apparently you can’t save a table in datastore.
DataStoreService: CantStoreValue: Cannot store Array in data store. Data stores can only accept valid UTF-8 characters. API: SetAsync, Data Store: PlotItems
104: Cannot store Array in data store. Data stores can only accept valid UTF-8 characters.
Script throws an error when trying to save plot items
Is there a different way to save what items are on the plot (and their position) and load them in? I’ve seen many games do something like this so there must be something I’m missing.
local data = game:GetService("DataStoreService")
local store = data:GetDataStore("PlotItems")
game.Players.PlayerAdded:Connect(function(plr)
wait(1)
for i,v in pairs(game.Workspace.Plots:GetChildren()) do
if v.Owner.Value == nil or "" then
v.Owner.Value = plr.Name
local plot = Instance.new("ObjectValue")
plot.Parent = plr
plot.Name = "Plot"
plot.Value = v
plr.Character.HumanoidRootPart.CFrame = v.Plot.CFrame + Vector3.new(0,5,0)
local items = store:GetAsync(plr.UserId)
if items then
for i,v in pairs(items) do
local clone = v:Clone()
clone.Parent = plr.Plot.Value
end
end
break
end
end
end)
game.Players.PlayerRemoving:Connect(function(plr)
wait(0.1)
local plot = plr.Plot
local parts = workspace:GetPartBoundsInBox(plot.Value.Save.CFrame, plot.Value.Save.Size)
local items = {}
for i,v in pairs(parts) do
if v:FindFirstChild("Item") then
table.insert(items, v)
end
end
local success, err = pcall(function()
store:SetAsync(plr.UserId, items)
end)
if success then
print("Saved")
else
warn(err)
end
end)
You can save arrays in a DataStore, the problem is that you’re trying to save an Instance which can’t be encoded into a JSON string. What you can do is to save the object’s name or some sort of route to the object (with a string).
As you may expect, you can’t save a Vector3 value either, so you will have to use a programming technique called serialization to save the position. Basically, just create a dictionary with each of the Vector3 axis’ values, so it can be encoded into JSON.
local function SerializeVector3(v3: Vector3): {[string]: number}
local t = { }
t.X = v3.X
t.Y = v3.Y
t.Z = v3.Z
return t
end
local function SaveData(plr)
--// Your variables
local plot = plr.Plot
local parts = workspace:GetPartBoundsInBox(plot.Value.Save.CFrame, plot.Value.Save.Size)
--// Save data
local Data = { }
for _, v in ipairs(parts) do
if v:FindFirstChild("Item") then
Data[v.Name] = {
Position = SerializeVector3(v.Position);
}
end
end
pcall(function()
store:SetAsync(plr.UserId, Data)
end)
end
Everything applied to your whole script would be something like this (didn’t debug, may have errors):
local data = game:GetService("DataStoreService")
local store = data:GetDataStore("PlotItems")
local function SerializeVector3(v3: Vector3): {[string]: number}
local t = { }
t.X = v3.X
t.Y = v3.Y
t.Z = v3.Z
return t
end
local function UnserializeVector3(t: {[string]: number}): Vector3
return Vector3.new(t.X, t.Y, t.Z)
end
local function SaveData(plr)
--// Your variables
local plot = plr.Plot
local parts = workspace:GetPartBoundsInBox(plot.Value.Save.CFrame, plot.Value.Save.Size)
--// Save data
local Data = { }
for _, v in ipairs(parts) do
if v:FindFirstChild("Item") then
Data[v.Name] = {
Position = SerializeVector3(v.Position);
}
end
end
pcall(function()
store:SetAsync(plr.UserId, Data)
end)
end
game.Players.PlayerAdded:Connect(function(plr)
for i, v in pairs(game.Workspace.Plots:GetChildren()) do
if v.Owner.Value == nil or v.Owner.Value == "" then
v.Owner.Value = plr.Name
local plot = Instance.new("ObjectValue")
plot.Parent = plr
plot.Name = "Plot"
plot.Value = v
plr.Character.HumanoidRootPart.CFrame = v.Plot.CFrame + Vector3.new(0,5,0)
local Data
local Success = pcall(function()
Data = store:GetAsync(plr.UserId) or { }
end)
if Success then
for name, data in pairs(Data) do
local ObjectsParent = game:GetService("ServerStorage").Objects --// Change this to your route.
local Object = ObjectsParent[name]
local NewObject = Object:Clone()
NewObject.Position = UnserializeVector3(data.Position)
NewObject.Parent = plr.Plot.Value
end
break
end
end
end)
game.Players.PlayerRemoving:Connect(SaveData)
game:BindToClose(function()
for _, plr in ipairs(game.Players:GetPlayers()) do
SaveData(plr)
end
end)
PS: Remember to change line 59 to wherever your objects are located and can be cloned from.