Help With Data Saving Script

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 want to make my save data script more efficient so that it saves the player’s leaderstats with fewer requests.

  2. What is the issue? Include screenshots / videos if possible!
    I keep getting an error message in testing if I change my stats too fast:

“DataStore request was added to queue. If request queue fills, further requests will be dropped. Try sending fewer requests.”

This is with one player in Studio testing, so it is probably even worse with a full server and stats changing every few seconds…

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I went through multiple tutorials and was able to create a leaderboard/crafting script that works, but sends a datastore request every time a player’s stat value changes. I am pretty new to scripting, and I know it is inefficient, but I am unsure how to improve it. In my game, there are four different resources as leaderstats: “Wood”, “Stone”, “Iron”, and “Explorite”, and all are changed frequently as the player uses their tools to mine the resources scattered around the map. I also have a tool-crafting section of script at the bottom, as in my game you can use resources to craft tools. When a player presses a TextButton in the GUI while having enough resources, the tool gets crafted and the required resources are taken.

This is my current script (a regular Script in ServerScriptService). I also have more of the crafting stuff in other places: crafting recipes ModuleScript, a RemoteFunction, and a folder of all my tools all in ReplicatedStorage. The crafting system works great, but the materials data save part is really inefficient.

	-----------------------Variables/Setup--------------------------------

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Tools = ReplicatedStorage:WaitForChild("Tools")
local CraftTool = ReplicatedStorage:WaitForChild("CraftTool")
local craftingInfo = require(ReplicatedStorage:WaitForChild("CraftingInfo"))
local datastore = game:GetService("DataStoreService")
local ds1 = datastore:GetDataStore("WoodSaveSystem")
local ds2 = datastore:GetDataStore("StoneSaveSystem")
local ds3 = datastore:GetDataStore("IronSaveSystem")
local ds5 = datastore:GetDataStore("ExploriteSaveSystem")

game.Players.PlayerAdded:connect(function(player)
	local leaderstats = Instance.new("Folder", player)
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	-------------------Data Saving System-----------------------------
	
	local Wood = Instance.new("IntValue", leaderstats)
	Wood.Name = "Wood"
	
	local Stone = Instance.new("IntValue", leaderstats)
	Stone.Name = "Stone"
	
	local Iron = Instance.new("IntValue", leaderstats)
	Iron.Name = "Iron"
		
	local Explorite = Instance.new("IntValue", leaderstats)
	Explorite.Name = "Explorite"
	
	
	-- data saves
	
	Wood.Value = ds1:GetAsync(player.UserId) or 10
	ds1:SetAsync(player.UserId, Wood.Value)
	
	Stone.Value = ds2:GetAsync(player.UserId) or 0
	ds2:SetAsync(player.UserId, Stone.Value)
	
	Iron.Value = ds3:GetAsync(player.UserId) or 0
	ds3:SetAsync(player.UserId, Iron.Value)	
	
	Explorite.Value = ds5:GetAsync(player.UserId) or 0
	ds5:SetAsync(player.UserId, Explorite.Value)
	
	-- update data saves
	
	Wood.Changed:connect(function()
		ds1:SetAsync(player.UserId, Wood.Value)
	end)
	
	Stone.Changed:connect(function()
		ds2:SetAsync(player.UserId, Stone.Value)
	end)
	
	Iron.Changed:connect(function()
		ds3:SetAsync(player.UserId, Iron.Value)
	end)
	
	
	Explorite.Changed:connect(function()
		ds5:SetAsync(player.UserId, Explorite.Value)
	end)
	
	--
end)

	-------------------Crafting System----------------------

	--function
CraftTool.OnServerInvoke = function(player, toolName)
	local leaderstats = player.leaderstats
	--materials
	local Stone = leaderstats.Stone
	local Wood = leaderstats.Wood
	local Iron = leaderstats.Iron
	local Explorite = leaderstats.Explorite
	--crafted?
	local crafted = false
	
	
	--craft the tool--
	
for i, v in pairs (craftingInfo[toolName]) do
		local material = leaderstats:FindFirstChild(i)
		
		if material.Value >= v then
			--tool into player backpack
			material.Value = material.Value - v
			local tool = Tools:FindFirstChild(toolName):Clone()
			tool.Parent = player.Backpack
			local startertool = Tools:FindFirstChild(toolName):Clone()
			startertool.Parent = player.StarterGear
			print ("toolcrafted")
			
		else 
			--not enough
			print ("not enough")
			
			end
		end
	end

I can post the other scripts if necessary, but I hope this works! Thank you!

2 Likes

I would not recommend saving everytime a stat is changed. Only save when the game is closing or the players leaves.

3 Likes
1 Like

That might work, but what happens if the game crashes? Will my player’s data save? I think an autosave every 3 minutes or so would be great, but what would be the best way to do that?

1 Like

that is what the closing part of the script will do. Use it like game:BindToClose

1 Like

I highly suggest to stay away from playerstats and instead rely on a table for the stats you want to assign the players. Make sure it is in a module so it can be read/write to by other scripts. A third-party data saving module only does so good. You’re better off taking the time and learning how to make one yourself from scratch like what you’re doing.

2 Likes

As I said, I am new to scripting. I used tutorials to help me make my current script, and I barely understand regular DataStores. I have never heard of nor know how to use DataStore2.

1 Like

I’m pretty new to scripting, where would game:BindToClose go in my script? Sorry for taking so long…

Here are a bunch of tutorials that’ll help you out for making datastores and using modules:

Custom Events and Callbacks | Documentation - Roblox Creator Hub (Use this to send changes of a player’s data to that player).

Bind to close will be outside of any other functions. It is like PlayerRemoving but includes all players.

Sorry for not being so specific in my original post. What I probably need is an autosave system that saves all of a player’s leaderstats every few minutes, using most of my current script if possible.

you dont need to worry about autosave as it will always save players data with game:BindToClose and game.Players.PlayerRemoving. I have personally, even when lagging out, never lost data with these 2 functions.

1 Like

Ok… so where would I put that in my script? I am obviously very new to scripting and used two tutorials to make this: one for the crafting/leaderstats system and one for the DataSaves.

I was also a beginner and I suffered a lot trying to make the regular DataStore work.
DataStore2 is much easier, more intuitive, and much more reliable.
Just read the tutorials there and you will have a peaceful life from there …

DataStore2 is much more complex and harder to learn. I wouldn’t recommend using DS2 for beginners.

Well, for me, as a beginner, it was just the opposite!
Very easy to learn, just read the examples there: DataStore2

So would removing the Material.Changed part of my script work?

yes. Because that itself is probably what is the problem with the datastore not saving well as there would be too many requests and they would start to drop the requests.

1 Like

But how should I add something like PlayerRemoving to save all of a player’s stats as they leave???

it would be something like this:

game.Players.PlayerRemoving:Connect(function(player)
datastore:SetAsync(player.UserId, player.leaderstats.Coins.Value)
end)

Also, this is just an example. You need to edit it yourself to make it work.

1 Like