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 5 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.

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)
0
did u try using a remoteevent to change the value? NickAtNick 163 — 5y

1 answer

Log in to vote
2
Answered by 5 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.

-- 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!


Ad

Answer this question