Help with datastore error?

I am in the process of creating a datastore system for my game, but I keep running into the error pictured below:

error

This is very frusturating, as I have to wait 30 seconds every time I click the stop button in my studio session. I’ve looked into many posts regarding this topic but I haven’t been able to find a solution.

Just for some context, I am trying to store information about randomly generated NPC models that are unique to the player.

Here is the code:

-- Services
local dss = game:GetService("DataStoreService")
local playerDataStore = dss:GetDataStore("PlayerData")
local plrs = game:GetService("Players")
local wrkspce = game:GetService("Workspace")
local cs = game:GetService("CollectionService")
local runservice = game:GetService("RunService")

-- Functions
local function saveData(player: Player)
	local key = ("Player: " ..tostring(player.UserId))
	local data = {}
	
	for _, item in pairs(player:WaitForChild("PlayerNPCFolder"):GetChildren()) do
		if item:IsA("Model") then
			local bodycolors = {}
			local gender
			
			for _, bodypart in pairs(item:GetChildren()) do
				if bodypart.Name == "Head" or bodypart:IsA("MeshPart") then
					
					local colors = {
					math.floor(bodypart.Color.R*255), 
					math.floor(bodypart.Color.G*255), 
					math.floor(bodypart.Color.B*255)}
					
					table.insert(bodycolors, colors)
				end
			end
			
			for _, tag in pairs(cs:GetTags(item)) do
				if tag == "Male" or tag == "Female" then
					gender = tag
				end
			end
			
			table.insert(data, {
				item.Name,
				item:FindFirstChildWhichIsA("Accessory"),
				bodycolors,
				gender
			})
		end
	end
	
	local success, err

	repeat
		success, err = pcall(function()
			playerDataStore:UpdateAsync(key, function()
				return data
			end)
		end)
		task.wait()
	until success

	if not success then
		warn("Failed to save "..player.Name.."'s data! (" ..tostring(player.UserId).. ")")
	end
	print(data)	
end

local function loadData(player: Player)
	local key = ("Player: " ..tostring(player.UserId))
	local success, err
	local data
	
	-- Game Folder
	local workspaceFolder = Instance.new("Folder")
	workspaceFolder.Parent = wrkspce.CurrentNPCs
	workspaceFolder.Name = player.Name
	
	-- Player Folders
	local leaderstats = Instance.new("Folder")
	leaderstats.Parent = player
	leaderstats.Name = "leaderstats"
	
	local plrNPCs = Instance.new("Folder")
	plrNPCs.Parent = player
	plrNPCs.Name = "PlayerNPCFolder"
	
	local deceasedPlrNPCs = Instance.new("Folder")
	deceasedPlrNPCs.Parent = player:WaitForChild("PlayerNPCFolder")
	deceasedPlrNPCs.Name = (player.Name.. "'s Deceased NPCs")
	
	repeat
		success, err = pcall(function()
			data = playerDataStore:GetAsync(key)
		end)
	until success or not plrs:FindFirstChild(player.Name)
	
	if success then
		for _, items in pairs(data) do
			if nil then
				print(player.Name.. " has no data")
			else
				-- stuff for later
			end
			print(data)
		end
	end
end

-- Events
plrs.PlayerAdded:Connect(loadData)
plrs.PlayerRemoving:Connect(saveData)
game:BindToClose(function()
	for _, plr: Player in pairs(plrs:GetPlayers()) do
		saveData(plr)
	end
end)

This is my first time working with datastores, so if there is anything I could be doing better or more efficiently, feel free to let me know. Thanks!

2 Likes
game:BindToClose(function()
	for _, plr: Player in pairs(plrs:GetPlayers()) do
coroutine.wrap(saveData)(plr)
end
end)

First of all I see this. coroutine.wrap allows the function to go without waiting for other players. So like if you had 3 players, the third person would have to wait, but you can’t wait if the game is closing really. This may be the main issue you are having

Because you have a million folders. I am going to make a simple function to help us out

local function MakeFolder(Name, Parent)
	local Folder = Instance.new("Folder", Parent)
	Folder.Name = Name
end

Lastly, with the repeat functions, you may be overloading your services. So what I am going to do is add a limit to that. If it doesn’t save after 5 tries then we keep the old data, and if we can’t load the data with in 5 tries, (5 tries is a LOT, if you are coming across this still, I would even lower the number, or not repeating it AT ALL. ). I also am Adding a wait time, as getting and saving data can take a little bit of time, and may not get the success in time before another load. We kick the player right away so they don’t reset their data…

So you final script looks like this

local function MakeFolder(Name, Parent)
	local Folder = Instance.new("Folder", Parent)
	Folder.Name = Name
end

local dss = game:GetService("DataStoreService")
local playerDataStore = dss:GetDataStore("PlayerData")
local plrs = game:GetService("Players")
local wrkspce = game:GetService("Workspace")
local cs = game:GetService("CollectionService")
local runservice = game:GetService("RunService")

-- Functions
local function saveData(player: Player)
	local key = ("Player: " ..tostring(player.UserId))
	local data = {}

	for _, item in pairs(player:WaitForChild("PlayerNPCFolder"):GetChildren()) do
		if item:IsA("Model") then
			local bodycolors = {}
			local gender

			for _, bodypart in pairs(item:GetChildren()) do
				if bodypart.Name == "Head" or bodypart:IsA("MeshPart") then

					local colors = {
						math.floor(bodypart.Color.R*255), 
						math.floor(bodypart.Color.G*255), 
						math.floor(bodypart.Color.B*255)}

					table.insert(bodycolors, colors)
				end
			end

			for _, tag in pairs(cs:GetTags(item)) do
				if tag == "Male" or tag == "Female" then
					gender = tag
				end
			end

			table.insert(data, {
				item.Name,
				item:FindFirstChildWhichIsA("Accessory"),
				bodycolors,
				gender
			})
		end
	end

	local success, err
local FailCount = 5
	repeat
		success, err = pcall(function()
			playerDataStore:UpdateAsync(key, function()
				return data
			end)
		end)
		task.wait(3)
		FailCount =- 1
	until success or FailCount <= 0

	if not success then
		warn("Failed to save "..player.Name.."'s data! (" ..tostring(player.UserId).. ")")
	end
	print(data)	
end

local function loadData(player: Player)
	local key = ("Player: " ..tostring(player.UserId))
	local success, err
	local data

	-- Game Folder
	MakeFolder(player.Name, wrkspce.CurrentNPCs)
	MakeFolder("leaderstats", player)
	MakeFolder("PlayerNPCFolder", player)
	MakeFolder(player.Name.. "'s Deceased NPCs", player:WaitForChild("PlayerNPCFolder"))

	local FailCount = 5
	repeat
		success, err = pcall(function()
			data = playerDataStore:GetAsync(key)
		end)
		task.wait(5)
		FailCount -= 1
	until success or not plrs:FindFirstChild(player.Name) or FailCount <= 0
	if FailCount <= 0 then
		player:Kick("Fail to load your data! Please try again")
	end

	if success then
		for _, items in pairs(data) do
			if nil then
				print(player.Name.. " has no data")
			else
				-- stuff for later
			end
			print(data)
		end
	end
end

-- Events
plrs.PlayerAdded:Connect(loadData)
plrs.PlayerRemoving:Connect(saveData)
game:BindToClose(function()
	for _, plr: Player in pairs(plrs:GetPlayers()) do
		coroutine.wrap(saveData)(plr)
	end
end)

Hopefully this helps and let me know if you still are having Errors or new errors!

1 Like

Thanks for your response! I tried out your code, and it seems to work great. I am no longer encountering the error when I stop the studio session.

However, the script isnt actually saving any data, and I’m currently trying to figure out why. If you happen to spot the reason why this is happening, I’d be really grateful if you could point it out to me. Other than that, thank you very much for your suggestions!

2 Likes

Let me take a quick look and see. I’m not my computer right now but yea

2 Likes

Was your data ever saving, like even with the error you had before?

Edit: Is there a reason your using UpdateAsync rather then SetAsync?

1 Like

At one point it was, but I remember there being a problem with it. I made a few changes here and there along the way and that’s when I encountered the previous error. I guess I was too focused on that error I never even realized that the data wasn’t even saving.

I was using UpdateAsync rather than SetAsync because I was told that it was just a better way to save data. I wasn’t given any proof to go along with that though, so it could be completly false for all I know. I just tested SetAsync and nothing changed.

A lot of people say that UpdateAsync is better, and that’s alright. Here, it doesn’t make too much of a difference, rather Update async is more used for like leveling up, where the value will almost never goes down, but it doesn’t mean it couldn’t be used here. I want you to change what I did and it should be something like this

local success, err
		success, err = pcall(function()
			playerDataStore:UpdateAsync(key, function()
				return data
			end)
		end)

if not success then
		warn("Failed to save "..player.Name.."'s data! (" ..tostring(player.UserId).. ")")
	end
	print(data)	
end

Let me know if this works! (this is apart of your code in the save data function)

Just tried it… now the warn is being called everytime I leave. I can tell we are getting closer though, I appreicate the help!

hmm. Just because I want to make sure it’s not because of other factors, try this. It may not do anything, but I will try.


	local	success, err = pcall(function()
			playerDataStore:UpdateAsync(key, function()
				return data
			end)
		end)

if not success then
		warn("Failed to save "..player.Name.."'s data! (" ..tostring(player.UserId).. ")")
	end
	print(data)	
end

Let me know if it fixed it?

1 Like

Nope, still not saving any data.

I’ve barely used BindToClose myself but the error makes me believe that you can’t save things to DataStores as soon as the game closes. What if game.Players.PlayerRemoving would do the data saving since closing the server would kick everyone out? Wouldn’t that be able to handle that?

When BindToClose happens, it doesn’t fire PlayerRemoving, causing all data to be lost from players in the server when the server is shut down.

And we do still have playing removing in these lines of code.

After a server closes, you have 30 seconds before everything stops and end. So you have 30 seconds to save everyones data. Saving the same data 2 times isn’t too much of an issue unless your having issues with the max amount of them.

I believe BindToClose gives a thirty second “grace period” to the server when it shuts down in order to execute any code that you would need to run. The problem with only using PlayerRemoving is that it wont be able to execute in time for each player before the server is shut down.

Ah I didn’t know that. Thanks for the explaination.
I’m not sure what else could be done then.

2 Likes

You mean that, when server shutdown happens PlayerRemoving is not firing?

Yup. Everyone leaves without firing The player removing function

I’m more use to SetAsync, So lets try it.

	local	success, err = pcall(function()
			playerDataStore:SetAsync(key, data)
		end)

if not success then
		warn("Failed to save "..player.Name.."'s data! (" ..tostring(player.UserId).. ")")
else 
print("Saved Data!")
	end
	print(data)	
end

forget to remove the extra end, you should be good now

I just made a test in a real server, using PlayerRemoving event, and from the website, shutdown all server, PlayerRemoving fired saving the DSS

Tried it, unfortunately still not working. I tested it without the BindToClose function and it still didn’t save, which is leading me to believe that it is not the source of the problem. Maybe its the data im trying to store? I am storing multiple tables inside tables, idk if that makes a difference or not.

try to use the datastore2, How to use DataStore2 - Data Store caching and data loss prevention it is very simple and easy to use and you dont need to code all datastore systems, all is done just use module functions from the datastore2

2 Likes