Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

My DataStore works if the valueChanges 1by1 but not when it changes like 1000at a time how do I fix?

My XP doesn't save if I add myself some money through the explorer in the studio even if I team test, but if I earn the XP slowly like it's supposed to it saves. Xpn and lvl doesn't save at all.

RebirthData = game:GetService("DataStoreService"):GetDataStore("Data1911")

XpData = game:GetService("DataStoreService"):GetDataStore("Data1912")

KillsData = game:GetService("DataStoreService"):GetDataStore("Data1913")

  

DeathsData = game:GetService("DataStoreService"):GetDataStore("Data1914")

  

LvlData = game:GetService("DataStoreService"):GetDataStore("Data1915")

  

XpnData = game:GetService("DataStoreService"):GetDataStore("Data1916")

  

-----------------------------------------------------------------------------------

game.Players.PlayerAdded:Connect(function(plr)

local LB = Instance.new("Folder", plr)

LB.Name = "leaderstats"

local K = Instance.new("IntValue", LB)

K.Name = "Kills"

K.Value = KillsData:GetAsync(plr.userId) or 0

  

local Xp = Instance.new ("IntValue", plr)

Xp.Name = "XP"

Xp.Value = XpData:GetAsync(plr.userId) or 0

local lvl = Instance.new ("IntValue", plr)

lvl.Name = "lvl"

lvl.Value = LvlData:GetAsync(plr.userId) or 1

local D = Instance.new("IntValue", LB)

D.Name = "Deaths"

D.Value = DeathsData:GetAsync(plr.userId) or 0

local Xpn = Instance.new("IntValue", plr)

Xpn.Name = "XpNeeded"

Xpn.Value = XpnData:GetAsync (plr.userId) or 50

Xpn.Value = 50

  

  

local r = Instance.new("IntValue", LB)

r.Name = "Rebirths"

r.Value = RebirthData:GetAsync(plr.userId) or 0

  

plr.CharacterAdded:Connect(function(char)

local humanoid

repeat

humanoid = char:FindFirstChild("Humanoid")

wait()

until humanoid

humanoid.Died:Connect(function()

D.Value = D.Value+1

local tag = humanoid:FindFirstChild("creator")

if tag then

local killer = tag.Value

if killer then

killer.leaderstats.Kills.Value = killer.leaderstats.Kills.Value + 1

killer.XP.Value = killer.XP.Value + math.random (5,10)

  

end

end

end)

end)

end)

game.Players.PlayerRemoving:Connect(function(plr)

XpData:SetAsync(plr.userId, plr.XP.Value)

KillsData:SetAsync(plr.userId, plr.leaderstats.Kills.Value)

DeathsData:SetAsync(plr.userId, plr.leaderstats.Deaths.Value)

RebirthData:SetAsync(plr.userId, plr.leaderstats.Rebirths.Value)

LvlData:SetAsync(plr.userId, plr.lvl.Value)

XpnData:SetAsync(plr.userId, plr.XpNeeded.Value)

end)

1 comment

0

did u try using a remoteevent to change the value?

1 answer

✓ Accepted answer

What's wrong?

As you may already know, data stores are used to save data so that they can be loaded in again whenever a player joins the game.

Data stores are like tables, you can set a key to a value. This value can be a string, number, or a table. Conventionally, tables are used as they can store much more than a string or number can.

-- Example of how tables are similar to how datastores work
local tab = {} -- a table
tab["Key"] = "Value" -- a value can be set to a key
local tabValue = tab["Key"] -- the value of a key can be accessed

local DsService = game:GetService("DataStoreService")
local example = DSService:GetDataStore("Example")
example:SetAsync("Key", "Value") -- a value can be set to a key
local exValue = example:GetAsync("Key") -- the value of a key can be accessed

This not only means that you can reduce all your data stores into one, but it also helps to solve your issue of data not saving. This is due to the limitations DataStores have. You can only save data to a key a limited amount of times within a time frame. This is to prevent people from sending thousands of requests for Roblox to save their data which would clog up Roblox's servers.

For more detailed information on these limits, click here.

Solution

You have to restructure how your data storage system works. Firstly, create a main data store that would contain every player's data. Since the data you have seems to be stats of a player, we'll call it stats.

local DsService = game:GetService("DataStoreService")
local Stats = DsService:GetDataStore("Stats")

Then, we set the player's data whenever they join. We will be using their user id as the key. If they have joined the first time, they would not have a value associated with their key, so attempting to use GetAsync would return nil. So we would set their data as the default data, which we would need to create as such:

local DefaultData = {
	["Xp"] = 0,
	["XpNeeded"] = 0,
	["Kills"] = 0,
	["Deaths"] = 0,
	["Level"] = 0
}

Now we can set up the PlayerAdded event

local Players = game.Players
Players.PlayerAdded:Connect(function(plr)
	-- Set the variable data to be the player's current table of stats
	-- If they joined for the first time, GetAsync would return nil
	-- and the variable data will be set to DefaultData
	local data = Stats:GetAsync(plr.UserId) or DefaultData
	
	-- Create the leaderstats folder to store the player's stats
	local leaderstats = Instance.new("Folder")
	leaderstats.Parent = plr
	leaderstats.Name = "leaderstats"
end)

We have to add each stat that the player has in their table of data. However, there are 5 pieces of data. Xp, XpNeeded, Kills, Deaths and Level. If we were to create the ValueObject of each stat individually, we would end up with a long series of repeated code which would make it difficult to read.

We can solve this using a for loop that goes through the player's data, create the ValueObjects based on the data type of the value, as such:

Players.PlayerAdded:Connect(function(plr)
	local data = Stats:GetAsync(plr.UserId) or DefaultData
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Parent = plr
	leaderstats.Name = "leaderstats"
	
	for key, value in pairs(data) do
		-- Initialise the variable valueObject to store
		-- the value instance later on based on the value's data type
		local valueObject
		
		if type(value) == "string" then
			-- If value is a string, create a string value
			valueObject = Instance.new("StringValue")
		elseif type(value) == "number" then
			-- If value is a number, create a number value
			valueObject = Instance.new("NumberValue")
		end
		
		-- Set the valueObject's parent to leaderstats
		valueObject.Parent = leaderstats
		-- Set the valueObject's name to the key, which would be the stat's name
		valueObject.Name = key
		-- Set the valueObject's value
		valueObject.Value = value
	end
end)

Now we have to be able to set their data when it needs to be updated, then save it when they leave. But how can we do this without possibly using SetAsync/GetAsync multiple times within a limited time frame?

We create a table that contains every player's data from when they joined the game, and update it whenever it needs to. We then access this table whenever we need to save someone's data. This removes the need to use SetAsync whenever someone's stat is changed while they are still in-game.

We'll call this table sessionData, since it would hold the data of each player for that session

local sessionData = {}

Players.PlayerAdded:Connect(function(plr)
	local data = Stats:GetAsync(plr.UserId) or DefaultData
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Parent = plr
	leaderstats.Name = "leaderstats"
	
	for key, value in pairs(data) do
		local valueObject
		
		if type(value) == "string" then
			valueObject = Instance.new("StringValue")
		elseif type(value) == "number" then
			valueObject = Instance.new("NumberValue")
		end
		
		valueObject.Parent = leaderstats
		valueObject.Name = key
		valueObject.Value = value
	end
	
	-- Set the data as the value to the key (player's user id) in sessionData
	sessionData[plr.UserId] = data
end)

We're almost done! Now all there's left to do is saving the player's data when they leave the game, and automatically save players' data every minute or so while we're at it.

Players.PlayerRemoving:Connect(function(plr)
	-- Set the player's data by accessing sessionData
	Stats:SetAsync(plr.UserId, sessionData[plr.UserId])
end)

while true do
	for _, plr in pairs(game.Players:GetPlayers()) do
		Stats:SetAsync(plr.UserId, sessionData[plr.UserId])
	end
	
	wait(60)
end

And we're done! A simple, though not perfect, data storage system is now complete. There are many ways to improve this data store (catching potential errors from SetAsync/GetAsync, a better way to automatically save without having to worry about limits, and much more), but that's a bit more advanced.

If you're adding on to the code, remember to write your code before the while loop! Otherwise, your code wouldn't run because the while loop would be running forever, not giving your code a chance to run :( (you can use the spawn function on the while loop if you really want to write code after it)


Remember to mark this as the answer if it solved your problem!


This discussion is preserved as it was. New answers, comments, and votes are closed.