Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

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

Asked by 6 years ago

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.

001RebirthData = game:GetService("DataStoreService"):GetDataStore("Data1911")
002 
003XpData = game:GetService("DataStoreService"):GetDataStore("Data1912")
004 
005KillsData = game:GetService("DataStoreService"):GetDataStore("Data1913")
006 
007   
008 
009DeathsData = game:GetService("DataStoreService"):GetDataStore("Data1914")
010 
011   
012 
013LvlData = game:GetService("DataStoreService"):GetDataStore("Data1915")
014 
015   
016 
017XpnData = game:GetService("DataStoreService"):GetDataStore("Data1916")
018 
019   
020 
021-----------------------------------------------------------------------------------
022 
023game.Players.PlayerAdded:Connect(function(plr)
024 
025local LB = Instance.new("Folder", plr)
026 
027LB.Name = "leaderstats"
028 
029local K = Instance.new("IntValue", LB)
030 
031K.Name = "Kills"
032 
033K.Value = KillsData:GetAsync(plr.userId) or 0
034 
035   
036 
037local Xp = Instance.new ("IntValue", plr)
038 
039Xp.Name = "XP"
040 
041Xp.Value = XpData:GetAsync(plr.userId) or 0
042 
043local lvl = Instance.new ("IntValue", plr)
044 
045lvl.Name = "lvl"
046 
047lvl.Value = LvlData:GetAsync(plr.userId) or 1
048 
049local D = Instance.new("IntValue", LB)
050 
051D.Name = "Deaths"
052 
053D.Value = DeathsData:GetAsync(plr.userId) or 0
054 
055local Xpn = Instance.new("IntValue", plr)
056 
057Xpn.Name = "XpNeeded"
058 
059Xpn.Value = XpnData:GetAsync (plr.userId) or 50
060 
061Xpn.Value = 50
062 
063   
064 
065   
066 
067local r = Instance.new("IntValue", LB)
068 
069r.Name = "Rebirths"
070 
071r.Value = RebirthData:GetAsync(plr.userId) or 0
072 
073   
074 
075plr.CharacterAdded:Connect(function(char)
076 
077local humanoid
078 
079repeat
080 
081humanoid = char:FindFirstChild("Humanoid")
082 
083wait()
084 
085until humanoid
086 
087humanoid.Died:Connect(function()
088 
089D.Value = D.Value+1
090 
091local tag = humanoid:FindFirstChild("creator")
092 
093if tag then
094 
095local killer = tag.Value
096 
097if killer then
098 
099killer.leaderstats.Kills.Value = killer.leaderstats.Kills.Value + 1
100 
101killer.XP.Value = killer.XP.Value + math.random (5,10)
102 
103   
104 
105end
106 
107end
108 
109end)
110 
111end)
112 
113end)
114 
115game.Players.PlayerRemoving:Connect(function(plr)
116 
117XpData:SetAsync(plr.userId, plr.XP.Value)
118 
119KillsData:SetAsync(plr.userId, plr.leaderstats.Kills.Value)
120 
121DeathsData:SetAsync(plr.userId, plr.leaderstats.Deaths.Value)
122 
123RebirthData:SetAsync(plr.userId, plr.leaderstats.Rebirths.Value)
124 
125LvlData:SetAsync(plr.userId, plr.lvl.Value)
126 
127XpnData:SetAsync(plr.userId, plr.XpNeeded.Value)
128 
129end)
0
did u try using a remoteevent to change the value? NickAtNick 163 — 6y

1 answer

Log in to vote
2
Answered by 6 years ago

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.

1-- Example of how tables are similar to how datastores work
2local tab = {} -- a table
3tab["Key"] = "Value" -- a value can be set to a key
4local tabValue = tab["Key"] -- the value of a key can be accessed
5 
6local DsService = game:GetService("DataStoreService")
7local example = DSService:GetDataStore("Example")
8example:SetAsync("Key", "Value") -- a value can be set to a key
9local 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.

1local DsService = game:GetService("DataStoreService")
2local 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:

1local DefaultData = {
2    ["Xp"] = 0,
3    ["XpNeeded"] = 0,
4    ["Kills"] = 0,
5    ["Deaths"] = 0,
6    ["Level"] = 0
7}

Now we can set up the PlayerAdded event

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

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:

01Players.PlayerAdded:Connect(function(plr)
02    local data = Stats:GetAsync(plr.UserId) or DefaultData
03     
04    local leaderstats = Instance.new("Folder")
05    leaderstats.Parent = plr
06    leaderstats.Name = "leaderstats"
07     
08    for key, value in pairs(data) do
09        -- Initialise the variable valueObject to store
10        -- the value instance later on based on the value's data type
11        local valueObject
12         
13        if type(value) == "string" then
14            -- If value is a string, create a string value
15            valueObject = Instance.new("StringValue")
16        elseif type(value) == "number" then
17            -- If value is a number, create a number value
18            valueObject = Instance.new("NumberValue")
19        end
20         
21        -- Set the valueObject's parent to leaderstats
22        valueObject.Parent = leaderstats
23        -- Set the valueObject's name to the key, which would be the stat's name
24        valueObject.Name = key
25        -- Set the valueObject's value
26        valueObject.Value = value
27    end
28end)

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

01local sessionData = {}
02 
03Players.PlayerAdded:Connect(function(plr)
04    local data = Stats:GetAsync(plr.UserId) or DefaultData
05     
06    local leaderstats = Instance.new("Folder")
07    leaderstats.Parent = plr
08    leaderstats.Name = "leaderstats"
09     
10    for key, value in pairs(data) do
11        local valueObject
12         
13        if type(value) == "string" then
14            valueObject = Instance.new("StringValue")
15        elseif type(value) == "number" then
16            valueObject = Instance.new("NumberValue")
17        end
18         
19        valueObject.Parent = leaderstats
20        valueObject.Name = key
21        valueObject.Value = value
22    end
23     
24    -- Set the data as the value to the key (player's user id) in sessionData
25    sessionData[plr.UserId] = data
26end)

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.

01Players.PlayerRemoving:Connect(function(plr)
02    -- Set the player's data by accessing sessionData
03    Stats:SetAsync(plr.UserId, sessionData[plr.UserId])
04end)
05 
06while true do
07    for _, plr in pairs(game.Players:GetPlayers()) do
08        Stats:SetAsync(plr.UserId, sessionData[plr.UserId])
09    end
10     
11    wait(60)
12end

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!


Ad

Answer this question