103: Array is not allowed in data stores [Solved!]

I have had a similar issue with trying to store cframes in datastores, and using HttpService:JSONEncode and HttpService:JSONDecode will not work on cframes. (I have tested this out don’t worry :wink: )

all datastore do is take a value and assign it to a variable in a datastore but he wants to store heap data in a data store but he cant do that by putting a table in a datastore so if he converts the table to a string then its valid data and it can be converted back to a table

what kind of data did u give it? can u give an example?

As a suggestion, could you please use punctuation? It makes sentences easier to read. :slight_smile:

There is a difference between arrays and dictionaries, arrays don’t have keys, dictionaries do

-- array
{"hi", 1092, false}
-- dictionary
{coins = 1029, level = 52, message = "hi"}
-- another way to do it is like this
{["coins"] = 1029, ["level"] = 52, ["message"] = "hi"}

Edit: Some people have pointed out already, you can’t store CFrames in data stores, you will have to take the values of the CFrame and store it

I don’t remember, as it was a while back, but, Ima go ahead and edit this post, and ping you when I find out.

EDIT:

Here is what happens when I load a JSON encoded cframe from a datastore:

nil… quite literally… nil

here is the code I use:

local dataStoreService = game:GetService("DataStoreService")
local dataStore = dataStoreService:GetDataStore("Idk")

game.Players.PlayerAdded:Connect(function(plr)
	local key = "key-"..plr.UserId
	
	local data
	pcall(function()
		data = dataStore:GetAsync(key)
	end)
	print(data)
	if data then
		print("Before JSON Decode: "..data)
		local decoded = game:GetService("HttpService"):JSONDecode(data)
		print("After JSON Decode: "..data)
	end
end)

game.Players.PlayerRemoving:Connect(function(plr)
	local key = "key-"..plr.UserId
	
	local data = workspace.Baseplate.CFrame
	
	print("Before JSON Encode: ")
	
	print(data)
	
	local encoded = game:GetService("HttpService"):JSONEncode(data)
	
	print("After JSON Encode: ")
	
	print(data)
	
	pcall(function()
		dataStore:SetAsync(key, data)
	end)
end)

@WeBuiltOurOwnWorld

1 Like

DataStores cannot store metatables such as CFrames, they can store dictionaries, but all keys must be strings, and the data contained must be a Lua datatype

These datatypes include:

  • string
  • number
  • boolean
  • array (as a dictionary with string or numeric keys)

The issue you are having is because you trying to store a CFrame as a value, which is not allowed in DataStores as this is a metatable object.

The best approach to saving a CFrame is to use a dictionary that hold the values of the CFrame, and then reconstruct the CFrame when it is next loaded

An example of this could be:

local SaveableCFrame = {X = CFrame.x, Y = CFrame.y, Z = CFrame.z ...}

Reconstruction involves taking the string keys and parsing them back into CFrame.new()

return CFrame.new(loadedData.X, loadedData.Y, loadedData.Z)
1 Like

I need to save more than one CFrame in one table. I mean, player can place on his slot ten parts. And i need to save them all. How to do that?

for _, model in pairs(workspace.Slots:FindFirstChild(player.Name).Items:GetChildren()) do
local primaryPart = model.PrimaryPart
local pos = primaryPart.Position - base.Position
local dir = primaryPart.Orientation
local modelData = {
Name = model.Name,
Position = {X = pos.X, Y = pos.Y, Z = pos.Z},
Orientation = {X = dir.X, Y = dir.Y, Z = dir.Z}
}
models[#models + 1] = modelData
end

Assuming you make a table for each model/ item/ part, you could put the cframe data in each
table.

save a table that stores all of the components of the CFrame

{slot1 = {}, slot2 = {}, slot3 = {}}

and so on, you could then store the data in there, however, I recommend rounding the numbers, so there is less chance of hitting the 260 000 character limit.

Why would you need to store the exact position and rotation however? Compression is a very strong principle when dealing with DataStores

Avoid using CFrame directly when dealing with datastores, usually Position and Orientation will do the job just fine (which are easier to turn into dictionaries aswell), if you must have CFrame, using a constructor function along the lines of

CFrame.new(data.Position.X, data.Position.Y, data.Position.Z) * CFrame.fromOrientation(data.Rotation.X, data.Rotation.Y, data.Rotation.Z)

will turn the dictionary back into a CFrame
(you may want to run your rotation through math.rad() since CFrame stores rotation as radians, not degrees)

Can’t you just do something like this to save the data:

function cframeToTable(cf)
	return {cf:GetComponents()};
end

function tableToCframe(t)
	return CFrame.new(table.unpack(t));
end
-- Save the data
local tbl = {}
for i, v in pairs(parts:GetDescendants()) do -- Idk if I typed "Descendants" correctly. xD
    table.insert(tbl, cframeToTable(v.CFrame))
end

-- Then use :SetAsync to save the data.

???

No, because this doesn’t have compression. You’d use a lot more data than you need.
Furthermore, does table.insert accept 1 argument?

1 Like

Can anybody just rework my script for it’s work correctly?

local ds = game:GetService("DataStoreService")
local slotDs = ds:GetOrderedDataStore("Slot")

game.Players.PlayerAdded:Connect(function(plr)
	local slot = workspace.Slots:FindFirstChild("EmptySlot")
	slot.Name = plr.Name
	slot.Owner.Value = plr
end)

game.Players.PlayerRemoving:Connect(function(plr)
	savePlace(plr)
	local slot = workspace.Slots:FindFirstChild(plr.Name)
	slot.Name = "EmptySlot"
	slot.Owner.Value = nil
end)

function savePlace(player)
	local models = {}
	local base = workspace.Slots:FindFirstChild(player.Name).PrimaryPart
	for _, model in pairs(workspace.Slots:FindFirstChild(player.Name).Items:GetChildren()) do	
		local primaryPart = model.PrimaryPart
		local pos = primaryPart.Position - base.Position
		local dir = primaryPart.Orientation
		local modelData = {
			Name = model.Name,
			Position = {X = pos.X, Y = pos.Y, Z = pos.Z},
			Orientation = {X = dir.X, Y = dir.Y, Z = dir.Z}
		}	
		models[#models + 1] = modelData
	end
	slotDs:SetAsync(player.UserId, models)
	print('done')
end

You can only save positive integers in ordered data stores

My bad, I went ahead and updated the post, and fixed it.

Ok… xd
As a suggestion, you could use roblox studio to program stuff, to minimize chance of making mistakes.

I’m doing that rn and it’s much easier than using this chatbox.

1 Like

I need to go so thank you all guys for helping me to solve this problem.
I’ll come back soon. So if somebody can please try to remake my script upper. Thank you.

Well, the thing I will change is this part

local slotDs = ds:GetOrderedDataStore("Slot")

OrderedDataStores can only store positive integers, it can’t store arrays or strings or anything except positive integers, so you should really be using a GlobalDataStore

local slotDs = ds:GetDataStore("Slot")
5 Likes

If i might ask, what is :GetOrderedDataStore for?