DataStore Tables and Cyclic Errors

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
  • I’m creating a data store for a character customizer. It’s my first time working on a data store and i’ve learned a lot, and rewritten this script 3 times now in different ways as I’ve learned. The main issue im having is saving 4 tables with different character slot’s information, into 1 table to store in a single data store.
  1. What is the issue? Include screenshots / videos if possible!
  • I kept getting this error:
    image
  • This happens when trying to save the data
  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
  • After looking into the error, it seemed that it simply was referencing itself in the table, but that didnt make sense as i never attempted to store their names. Once I printed the tables out, it shows that my player username was added to this table at some point and that’s what was causing this cyclic error. I’ve tried changing a bunch of different lines of code and printing in different places to try to see where it gets added but still can’t figure it out.

Here’s the code:

--|| SERVICES ||--
local players = game:GetService("Players")
local DSS = game:GetService("DataStoreService")
local rs = game:GetService("ReplicatedStorage")
local runService = game:GetService("RunService")

local data = DSS:GetDataStore("RPG")

--|| EVENTS ||--
local titleCharEvent = rs.Events.TitleCharEvent
local deleteCharEvent = rs.Events.DeleteCharEvent

--|| VARIABLES ||--
local dummy = workspace.CharCreatorArea.StarterCharacter
local parts = rs.CharCreatorParts

local CharSlot1 = {Hair = "",Face = "",BodyColor = "",Shirt = "",Pants = "", Active = false}
local CharSlot2 = {Hair = "",Face = "",BodyColor = "",Shirt = "",Pants = "", Active = false}
local CharSlot3 = {Hair = "",Face = "",BodyColor = "",Shirt = "",Pants = "", Active = false}
local CharSlot4 = {Hair = "",Face = "",BodyColor = "",Shirt = "",Pants = "", Active = false}
local PlayerDataTable = {}
--|| FUNCTIONS || --
players.PlayerAdded:Connect(function(plr)
	local success, returned = pcall(function()
		return data:GetAsync(plr.UserId)
	end)
	if success and returned then
		print("Data Loaded!")
		print(returned)
		print(returned[1])
		print(returned[2])
		print(returned[3])
		print(returned[4])
		if returned[1].Hair ~= "" then
			print("Char 1 Loaded")
			plr.PlayerGui:WaitForChild("Main").TitleScreen.Background.CharSlots.CharSlot1.Text = "Char 1"
			CharSlot1[plr.Name] = returned[1]
		else
			CharSlot1[plr.Name] = CharSlot1
		end
		if returned[2].Hair ~= "" then
			print("Char 2 Loaded")
			plr.PlayerGui:WaitForChild("Main").TitleScreen.Background.CharSlots.CharSlot2.Text = "Char 2"
			CharSlot2[plr.Name] = returned[2]
		else
			CharSlot2[plr.Name] = CharSlot2
		end
		if returned[3].Hair ~= "" then
			print("Char 3 Loaded")
			plr.PlayerGui:WaitForChild("Main").TitleScreen.Background.CharSlots.CharSlot3.Text = "Char 3"
			CharSlot3[plr.Name] = returned[3]
		else
			CharSlot3[plr.Name] = CharSlot3
		end
		if returned[4].Hair ~= "" then
			print("Char 4 Loaded")
			plr.PlayerGui:WaitForChild("Main").TitleScreen.Background.CharSlots.CharSlot4.Text = "Char 4"
			CharSlot4[plr.Name] = returned[4]
		else
			CharSlot4[plr.Name] = CharSlot4
		end
	else
		error(returned)
	end
end)
-----------------------------SAVE---------------------------------------------
function Save(plr)
	PlayerDataTable = {CharSlot1,CharSlot2,CharSlot3,CharSlot4}
	data:SetAsync(plr.UserId, PlayerDataTable)
	
	print("Saving")
end

game:BindToClose(function()
	if runService:IsStudio() then
		for i,v in ipairs(players:GetPlayers()) do
			Save(v)
		end
		wait(3)
	end
end)


----------------INVOKE--------------------
script.change.OnInvoke = function(plr, what, how, slot)
	task.wait()
	print(CharSlot1)
	if slot == 1 then
		CharSlot1[plr.Name][what] = how
	elseif slot == 2 then
		CharSlot2[plr.Name][what] = how
	elseif slot == 3 then
		CharSlot3[plr.Name][what] = how
	elseif slot == 4 then
		CharSlot4[plr.Name][what] = how
	end
end

------------------LOADING TITLE CHAR--------------------------
titleCharEvent.OnServerEvent:Connect(function(plr, slot)	
	if dummy:FindFirstChildOfClass("Accessory") then
		dummy:FindFirstChildOfClass("Accessory"):Destroy()
		dummy.Head:FindFirstChildOfClass("Decal"):Destroy()
		dummy:FindFirstChildOfClass("Shirt"):Destroy()
		dummy:FindFirstChildOfClass("Pants"):Destroy()
	end
	local data = data:GetAsync(plr.UserId)
	task.wait(.02)
	if slot == "Char 1" then
		--hair
		local newHair = parts.Hair:FindFirstChild(data[1].Hair):Clone()
		newHair.Parent = dummy
		--face
		local newFace = parts.Face:FindFirstChild(data[1].Face):Clone()
		newFace.Parent = dummy.Head
		--shirt
		local newShirt = parts.Shirt:FindFirstChild(data[1].Shirt):Clone()
		newShirt.Parent = dummy
		--pants
local newPants = parts.Pants:FindFirstChild(data[1].Pants):Clone()
		newPants.Parent = dummy
		--color
		local color = data[1].BodyColor
		dummy["Body Colors"].HeadColor = BrickColor.new(color)
		dummy["Body Colors"].TorsoColor = BrickColor.new(color)
		dummy["Body Colors"].LeftArmColor = BrickColor.new(color)
		dummy["Body Colors"].LeftLegColor = BrickColor.new(color)
		dummy["Body Colors"].RightArmColor = BrickColor.new(color)
		dummy["Body Colors"].RightLegColor = BrickColor.new(color)
	elseif slot == "Char 2" then
		--hair
		local newHair = parts.Hair:FindFirstChild(data[2].Hair):Clone()
		newHair.Parent = dummy
		--face
		local newFace = parts.Face:FindFirstChild(data[2].Face):Clone()
		newFace.Parent = dummy.Head
		--shirt
		local newShirt = parts.Shirt:FindFirstChild(data[2].Shirt):Clone()
		newShirt.Parent = dummy
		--pants
		local newPants = parts.Pants:FindFirstChild(data[2].Pants):Clone()
		newPants.Parent = dummy
		--color
		local color = data[2].BodyColor
		dummy["Body Colors"].HeadColor = BrickColor.new(color)
		dummy["Body Colors"].TorsoColor = BrickColor.new(color)
		dummy["Body Colors"].LeftArmColor = BrickColor.new(color)
		dummy["Body Colors"].LeftLegColor = BrickColor.new(color)
		dummy["Body Colors"].RightArmColor = BrickColor.new(color)
		dummy["Body Colors"].RightLegColor = BrickColor.new(color)
	elseif slot == "Char 3" then
		--hair
		local newHair = parts.Hair:FindFirstChild(data[3].Hair):Clone()
		newHair.Parent = dummy
		--face
		local newFace = parts.Face:FindFirstChild(data[3].Face):Clone()
		newFace.Parent = dummy.Head
		--shirt
		local newShirt = parts.Shirt:FindFirstChild(data[3].Shirt):Clone()
		newShirt.Parent = dummy
		--pants
		local newPants = parts.Pants:FindFirstChild(data[3].Pants):Clone()
		newPants.Parent = dummy
		--color
		local color = data[3].BodyColor
		dummy["Body Colors"].HeadColor = BrickColor.new(color)
		dummy["Body Colors"].TorsoColor = BrickColor.new(color)
		dummy["Body Colors"].LeftArmColor = BrickColor.new(color)
		dummy["Body Colors"].LeftLegColor = BrickColor.new(color)
		dummy["Body Colors"].RightArmColor = BrickColor.new(color)
		dummy["Body Colors"].RightLegColor = BrickColor.new(color)
	elseif slot == "Char 4" then
		--hair
		local newHair = parts.Hair:FindFirstChild(data[4].Hair):Clone()
		newHair.Parent = dummy
		--face
		local newFace = parts.Face:FindFirstChild(data[4].Face):Clone()
		newFace.Parent = dummy.Head
		--shirt
		local newShirt = parts.Shirt:FindFirstChild(data[4].Shirt):Clone()
		newShirt.Parent = dummy
		--pants
		local newPants = parts.Pants:FindFirstChild(data[4].Pants):Clone()
		newPants.Parent = dummy
		--color
		local color = data[4].BodyColor
		dummy["Body Colors"].HeadColor = BrickColor.new(color)
		dummy["Body Colors"].TorsoColor = BrickColor.new(color)
		dummy["Body Colors"].LeftArmColor = BrickColor.new(color)
		dummy["Body Colors"].LeftLegColor = BrickColor.new(color)
		dummy["Body Colors"].RightArmColor = BrickColor.new(color)
		dummy["Body Colors"].RightLegColor = BrickColor.new(color)
	end
end)

-------------------------------------RESET DUMMY------------------------------------------------

function dummyReset()
	task.wait()
	dummy:FindFirstChildOfClass("Shirt"):Destroy()
	dummy:FindFirstChildOfClass("Pants"):Destroy()
	dummy:FindFirstChildOfClass("Accessory"):Destroy()
	dummy.Head:FindFirstChildOfClass("Decal"):Destroy()
end

------------------------------------DELETE CHARACTER------------------------------------------
deleteCharEvent.OnServerEvent:Connect(function(plr, slot)
	if slot == 1 then
		CharSlot1[plr.Name] = CharSlot1
		plr.PlayerGui.Main.TitleScreen.Background.CharSlots.CharSlot1.Text = "Create a Character"
		dummyReset()
	elseif slot == 2 then
		CharSlot2[plr.Name] = CharSlot2
		plr.PlayerGui.Main.TitleScreen.Background.CharSlots.CharSlot2.Text = "Create a Character"
		dummyReset()
	elseif slot == 3 then
		CharSlot3[plr.Name] = CharSlot3
		plr.PlayerGui.Main.TitleScreen.Background.CharSlots.CharSlot3.Text = "Create a Character"
		dummyReset()
	elseif slot == 4 then
		CharSlot4[plr.Name] = CharSlot4
		plr.PlayerGui.Main.TitleScreen.Background.CharSlots.CharSlot4.Text = "Create a Character"
		dummyReset()

	end
end)

players.PlayerRemoving:Connect(Save)

This is my first time trying out data stores, so i’m sorry if any of this code is really bad. I’m definitely open to any advice with all of this!

In what line does the error show up?

The function Save(plr)

data:SetAsync(plr.UserId, PlayerDataTable)

I slowly start to get the problem you’re causing. I maybe have a result for that. Try this:

function Save(plr)
	local PlayerDataTable = {
		["CharSlot1"] = {},
		["CharSlot2"] = {},
		["CharSlot3"] = {},
		["CharSlot4"] = {}
	}
	PlayerDataTable["CharSlot1"] = CharSlot1
	PlayerDataTable["CharSlot2"] = CharSlot2
	PlayerDataTable["CharSlot3"] = CharSlot3
	PlayerDataTable["CharSlot4"] = CharSlot4
	data:SetAsync(plr.UserId, PlayerDataTable)

	print("Saving")
end

(sorry for late reply by the way)

There’s still a cyclic error, all of the character slots get my Username added to them which throws the error. It looks like it happens because of CharSlot1[plr.Name] = CharSlot1 but when i remove that, their table doesnt exist to be changed