Saving the parts and loading them back (plots)

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)
2 Likes

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
2 Likes

Hey, Any chance you can quickly Integrate?

1 Like

Do you mean entering the place? You can add me on discord if you prefer (sarchyx6)

2 Likes

I mea can you integrate the Serialization into the plot script.

1 Like

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.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.