(Simple) DataStore SetAsync with JSONEncoded Data

What do I want to achieve?

I want to save a table of values. I heard that you need to use JSON format.

What is the issue?

It’s only printing to “Made it here 3” (I had others of course that’s why It’s at 3)

image

Here is the full code:

local dataStoreService = game:GetService("DataStoreService")
local dataStore = dataStoreService:GetDataStore("leaderstats")
local httpService = game:GetService("HttpService")

game.Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local armor = Instance.new("IntValue")
	armor.Name = "Armor"
	armor.Parent = leaderstats
	
	local cash = Instance.new("IntValue")
	cash.Name = "Cash"
	cash.Parent = leaderstats
	
	local dataLoaded = Instance.new("BoolValue")
	dataLoaded.Name = "DataLoaded"
	dataLoaded.Parent = player
	
	local success, data = pcall(function()
		return dataStore:GetAsync(player.UserId)
	end)
	
	if success then
		print("Successfully got data for",player)
		dataLoaded.Value = true
		if data then
			local dataDecoded = httpService:JSONDecode(data)
			armor.Value = dataDecoded["Armor"]
			cash.Value = dataDecoded["Cash"]
		else
			armor.Value = 0
			cash.Value = 0
		end
	else
		print("Failed to get data for",player,data)
		dataLoaded.Value = false
		player:Kick("Rejoin please")
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	if player.DataLoaded.Value == true then
		local data = {
			["Cash"] = player.leaderstats.Cash.Value,
			["Armor"] = player.leaderstats.Armor.Value
		}
		local dataEncoded = httpService:JSONEncode(data)
		local success, message = pcall(function()
			print("Made it here 3")
			dataStore:SetAsync(player.UserId, dataEncoded)
			print("Made it here 4")
		end)
		
		if success then print("data saved for",player) else print("data did not save for",player,message) end
	end
end)

What Solutions Have I Tried so far?

  • I’ve tried many solutions. From my own solutions to related dev forum threads. Too many to list :grin:

Any help is appreciated, thanks!

1 Like
  1. You don’t need to use JSON format. SetAsync encodes the value sent to JSON already. Pass it a Lua table. Remove line 50 and you’ll have no issues saving that data table defined at 46-49. You do however, need to use JSON supported values (numbers, strings, tables). In your case, you’re fine, but don’t try to save a Vector3 or Color3 value, etc (userdata not supported by backend JSON encoding)
  2. If you’re only calling this on PlayerRemoving and expecting it to finish before the studio session ends, then that is your problem. PlayerRemoving will (sometimes) fire when you press Stop in studio, but unlike BindToClose, it will not yield. This means line 53 may not be running at all before the datamodel is closed. Below is a setup you could use that supports both PlayerRemoving and BindToClose
local function Save(player)
	if player.DataLoaded.Value == true then
		local data = {
			["Cash"] = player.leaderstats.Cash.Value,
			["Armor"] = player.leaderstats.Armor.Value
		}
		--local dataEncoded = httpService:JSONEncode(data) -- commented out as it's not needed.
		local success, message = pcall(function()
			print("Made it here 3")
			dataStore:SetAsync(player.UserId, data)
			print("Made it here 4")
		end)
		
		if success then
			print("data saved for",player)
		else
			print("data did not save for",player,message)
		end
	end
end

if not game:GetService("RunService"):IsStudio() then -- only call this outside of studio (when used in conjunction with a BindToClose save). If you remove this if statement both PlayerRemoving and BindToClose may fire the save when you press Stop, causing you to hit the write limit.
	game.Players.PlayerRemoving:Connect(Save)
end
game:BindToClose(function()
	for _,player in pairs(game.Players:GetPlayers()) do
		local s, e = pcall(Save, player) --pcall incase of error, we want to continue to loop other players
	end
end
4 Likes

Thank you! Although it works, I don’t know why :blush:

So basically
if not game:GetService("RunService"):IsStudio() then
is needed for Studio sessions?

If you remove that line, you will notice that studio yields for longer than ideal. This is because, when used with a BindToClose save call, it causes SetAsync to be throttled because you have a chance to be calling two saves at the exact same time. SetAsync only allows 1 call per key every 6 seconds :slight_smile:

1 Like