I don’t think there is too much documentation about this, you can read about it here, though, it’s in the first blue box.
can i get help with my datastore?
when i join the game every value just become 100 and cant save
here is my script:
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local dataStore = DataStoreService:GetDataStore("DataStoreName") --put your DataStore name here
local function setUp(player)
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local click = Instance.new("IntValue")
click.Name = "Clicks"
local diamond = Instance.new("IntValue")
diamond.Name = "Diamonds"
local rebirth = Instance.new("IntValue")
rebirth.Name = "Rebirths"
local data = dataStore:GetAsync(key)
click.Value = data or 0
diamond.Value = data or 100
rebirth.Value = data or 0
click.Parent = leaderstats
diamond.Parent = leaderstats
rebirth.Parent = leaderstats
leaderstats.Parent = player
end
local function save(player)
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
local clickValue = leaderstats.Clicks.Value
dataStore:SetAsync(key, clickValue)
local diamondValue = leaderstats.Diamonds.Value
dataStore:SetAsync(key, diamondValue)
local rebirthValue = leaderstats.Rebirths.Value
dataStore:SetAsync(key, rebirthValue)
end
end
local function onShutDown()
wait(1)
end
for _,player in ipairs(Players:GetPlayers()) do
coroutine.wrap(setUp)(player)
end
Players.PlayerAdded:Connect(setUp)
Players.PlayerRemoving:Connect(save)
game:BindToClose(onShutDown)
while true do
wait(60)
for _,player in ipairs(Players:GetPlayers()) do
coroutine.wrap(save)(player)
end
end
It’s the way you are setting data, you are overriding the recent value set each time. Instead, you should save your data in an array or dictionary:
(pseudo-code)
local data = GetAsync(userId) --(simplified, I think you should make this more secure)
--data is your array/dictionary now
click.Value = data[1] or default
diamond.Value = data[2] or default
rebirth.Value = data[3] or default
...
local tableToSave = {clickValue, diamondValue, rebirthValue}
SetAsync(userId, tableToSave) --(simplified, I think you should make this more secure)
sorry i am confused at where should i put that in?
It was pseudo-code, to implement it, you would probably change your setUp function to
local function setUp(player)
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local click = Instance.new("IntValue")
click.Name = "Clicks"
local diamond = Instance.new("IntValue")
diamond.Name = "Diamonds"
local rebirth = Instance.new("IntValue")
rebirth.Name = "Rebirths"
local data = dataStore:GetAsync(key)
click.Value = (data and data[1]) or 0
diamond.Value = (data and data[2]) or 100
rebirth.Value = (data and data[3]) or 0
click.Parent = leaderstats
diamond.Parent = leaderstats
rebirth.Parent = leaderstats
leaderstats.Parent = player
end
and your save function to
local function save(player)
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
local clickValue = leaderstats.Clicks.Value
local diamondValue = leaderstats.Diamonds.Value
local rebirthValue = leaderstats.Rebirths.Value
local toSave = {clickValue, diamondValue, rebirthValue}
dataStore:SetAsync(key, toSave)
end
end
Hey, thank you very much for your tutorial! It was extremely helpful and your code is actually really good.
Recently learning Lua (I come from Java) a huge pet peeve of mine is tutorials not using proper coding practices. It makes it more difficult to understand and generally adds a lot of extra and unnecessary code!
The only suggestion I have is that I would’ve liked you to show how to save multiple stats and items as well.
Although, I’m pretty sure I figured this out on my own by creating a table.
I’ll paste the code here in case anyone is interested to see how you can do it, or provide feedback into my method of doing it!
local Players = game:GetService('Players')
local ServerStorage = game:GetService('ServerStorage')
local DataStoreService = game:GetService('DataStoreService')
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local dataStore = DataStoreService:GetDataStore("2222")
local items = ReplicatedStorage:WaitForChild("Items")
local function waitForRequestBudget(requestType)
local currentBudget = DataStoreService:GetRequestBudgetForRequestType(requestType)
while currentBudget < 1 do
currentBudget = DataStoreService:GetRequestBudgetForRequestType(requestType)
wait(5)
end
end
local function setUp(player)
local userID = player.UserId
local key = "Player_" .. userID
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local coins = Instance.new("IntValue")
coins.Name = "Coins"
local strength = Instance.new("IntValue")
strength.Name = "Strength"
local inventory = Instance.new("Folder")
inventory.Name = "inventory"
inventory.Parent = player
local tool = Instance.new("StringValue")
tool.Name = "Tool"
tool.Value = "nil"
tool.Parent = inventory
local success, returnValue
repeat
waitForRequestBudget(Enum.DataStoreRequestType.GetAsync)
success, returnValue = pcall(dataStore.GetAsync, dataStore, key)
until success or not Players:FindFirstChild(player.Name)
if success then
print(returnValue)
if returnValue == nil then
returnValue = {
Coins = 0,
Strength = 0,
Tool = "Phone1"
}
end
coins.Value = returnValue["Coins"] or 0
strength.Value = returnValue["Strength"] or 0
player.inventory.Tool.Value = returnValue["Tool"] or "Phone1"
player.CharacterAdded:Connect(function()
if returnValue["Tool"] ~= nil or returnValue["Tool"] ~= "nil" then
for _, tool in ipairs(items:GetChildren()) do
if tool:IsA("Tool") and tool.Name == returnValue["Tool"] then
local clonedTool = tool:Clone()
clonedTool.Parent = player.Backpack
break
end
end
else
local clonedTool = items.Phone1:Clone()
clonedTool.Parent = player.Backpack
end
end)
coins.Parent = leaderstats
strength.Parent = leaderstats
leaderstats.Parent = player
else
print("Data Loading ERROR!!!")
end
end
local function save(player, donttWait)
local userID = player.UserId
local key = "Player_" .. userID
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
local coinValue = leaderstats.Coins.Value
local strengthValue = leaderstats.Strength.Value
local tool = player.inventory.Tool.Value
local dataTable = {
Coins = coinValue,
Strength = strengthValue,
Tool = tool
}
local success, returnValue
repeat
if not donttWait then
waitForRequestBudget(Enum.DataStoreRequestType.SetIncrementAsync)
end
success, returnValue = pcall(dataStore.UpdateAsync, dataStore, key, function()
return dataTable
end)
until success
if success then
print("Data Saved!")
else
print("Data Saving ERROR!!!")
end
end
end
for _, player in ipairs(Players:GetPlayers()) do
coroutine.wrap(setUp)(player)
end
-- Delays the game from shutting down by X seconds so the PlayerRemoving Event will 100% fire
local function onShutdown()
if RunService:IsStudio() then
wait(2)
else
local finished = Instance.new("BindableEvent")
local allPlayers = Players:GetPlayers()
local leftPlayers = #allPlayers
for _, player in ipairs(allPlayers) do
coroutine.wrap(function()
save(player)
leftPlayers -= 1
if leftPlayers == 0 then
finished:Fire()
end
end)()
end
finished.Event:Wait()
end
end
Players.PlayerAdded:Connect(setUp)
Players.PlayerRemoving:Connect(save)
game:BindToClose(onShutdown)
-- Auto-Save all Players Every 600 seconds
while true do
wait(600)
for _, player in ipairs(Players:GetPlayers()) do
coroutine.wrap(save)(player)
end
end
thank you for your guide! now i already understand!
This doesn’t work please help me : (
Elaborate please (send your code, your output and describe the problem).
Does this still work/are there any problems I’ll run into when using this? I wanna create my own datastore instead of just using ProfileService/whatever and this tutorial seems really helpful.
There could be, this is barely tested, I made this for the purpose of showing some techniques that can be used when making DataStores.
As I already said in the tutorial, I recommend using ProfileService/other known DataStore modules instead of this if you just want a safe DataStore in your game since they will probably be a lot safer, they are heavily tested because of the amount of people that use them which makes bugs more unlikely.
@GEILER123456 Hey, I’m very new to Datastore I only had only been using it for a week. So, I found this topic and use some solution and method but my problem still occur… why doesn’t my datastore save?
local Datastore = game:GetService("DataStoreService"):GetDataStore("Data")
local Save = {"Data1", "Data2"}
game.Players.PlayerAdded:Connect(function(player)
local folder = Instance.new("Folder")
local Brick = Instance.new("IntValue")
local Money = Instance.new("IntValue")
folder.Name = "leaderstats"
folder.Parent = player
Money.Value = 0
Money.Name = "Money"
Money.Parent = folder
Brick.Name = "Brick"
Brick.Value = 0
Brick.Parent = folder
local success, err = pcall(function()
Save[1] = Datastore:GetAsync(player.UserId)
Money.Value = Save[1]
Brick.Value = Save[2]
end)
if success then
print(player.Name .. " Data has been successfully loaded!")
else
print(string.upper(player.Name .. " Data was not save " .. err))
end
end)
game.Players.PlayerRemoving:Connect(function(player)
local success, err = pcall(function()
Datastore:SetAsync(player.UserId, Save)
player.leaderstats.Money.Value = Save[1]
player.leaderstats.Brick.Value = Save[2]
end)
if success then
print(player.Name .. " Data has been successfully saved!")
else
print(string.upper(player.Name .. " Data was unsuccessful while saving " .. err))
end
end)
Keep in mind that this is a serverscript, you are dealing with multiple players.
Save[1] = Datastore:GetAsync(player.UserId)
Money.Value = Save[1]
Brick.Value = Save[2]
doesn’t make much sense, same as
Datastore:SetAsync(player.UserId, Save)
player.leaderstats.Money.Value = Save[1]
player.leaderstats.Brick.Value = Save[2]
When using :GetAsync(), you want to receive a table, since you want to save two values.
When saving, you want to save a table with those two values.
Here, a DataStore very similar to the one in the first part of my tutorial, just modified a bit so it includes your Money and Brick values:
local Players = game:GetService("Players")
local DataStore = game:GetService("DataStoreService"):GetDataStore("Data")
local function setUp(player)
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = Instance.new("Folder")
local money = Instance.new("IntValue")
local brick = Instance.new("IntValue")
leaderstats.Name = "leaderstats"
money.Name = "Money"
brick.Name = "Brick"
local success, data = pcall(DataStore.GetAsync, DataStore, key)
if success then
print(data)
data = data or {0, 0} --if no data was saved, then 0 will be the default for money and brick
money.Value = data[1]
brick.Value = data[2]
money.Parent = leaderstats
brick.Parent = leaderstats
leaderstats.Parent = player
else
print("There was an error while getting the data: " .. data)
end
end
local function save(player)
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
local toSave = {leaderstats.Money.Value, leaderstats.Brick.Value}
local success, ret = pcall(DataStore.SetAsync, DataStore, key, toSave)
if success then
print("Successfully saved.")
print(toSave)
else
print("There was an error while saving: " .. ret)
end
end
end
local function onShutDown()
task.wait(2)
end
Players.PlayerAdded:Connect(setUp)
Players.PlayerRemoving:Connect(save)
game:BindToClose(onShutDown)
This is of course not safe yet,
so I recommend to use a popular DataStore module like ProfileService or make it safer (by adding retries etc., most of which I know I mentioned in the tutorial).
This by far is one of the best posts I’ve seen thus far on devfourm.
local success, ret, data
repeat
waitForRequestBudget()
success, ret = pcall(dataStore.UpdateAsync, dataStore, key, function(oldData)
oldData = oldData or default
if oldData.SessionLock then
--He is still sessionlocked, so just wait
if os.time() - oldData.SessionLock < 1800 then
--session is alive
ret = "Wait"
else
--session is dead, take over
oldData.SessionLock = os.time()
data = oldData
return data
end
else
oldData.SessionLock = os.time()
data = oldData
return data
end
end)
if ret == "Wait" then
wait(5)
end
until success or not Players:FindFirstChild(name)
Do you think there’s a better alternative way to do this?
When I used it in my script it the data came back nil
Are there any errors?
Are you using
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local dataStore = DataStoreService:GetDataStore("Players")
local default = {
SessionLock = false,
Cash = 0
}
local updateAsync = Enum.DataStoreRequestType.UpdateAsync
local function waitForRequestBudget()
local currentBudget = DataStoreService:GetRequestBudgetForRequestType(updateAsync)
while currentBudget < 1 do
currentBudget = DataStoreService:GetRequestBudgetForRequestType(updateAsync)
wait(5)
end
end
local function setUp(player)
local name = player.Name
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local cash = Instance.new("IntValue")
cash.Name = "Cash"
local success, ret, data
repeat
waitForRequestBudget()
success, ret = pcall(dataStore.UpdateAsync, dataStore, key, function(oldData)
oldData = oldData or default
if oldData.SessionLock then
--He is still sessionlocked, so just wait
if os.time() - oldData.SessionLock < 1800 then
--session is alive
ret = "Wait"
else
--session is dead, take over
oldData.SessionLock = os.time()
data = oldData
return data
end
else
oldData.SessionLock = os.time()
data = oldData
return data
end
end)
if ret == "Wait" then
wait(5)
end
until success or not Players:FindFirstChild(name)
if success then
cash.Value = data.Cash
cash.Parent = leaderstats
leaderstats.Parent = player
end
end
local function save(player, dontLeave, dontWait)
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
local cashValue = leaderstats.Cash.Value
local success
repeat
if not dontWait then
waitForRequestBudget()
end
success = pcall(dataStore.UpdateAsync, dataStore, key, function()
return {
SessionLock = dontLeave and os.time() or nil,
Cash = cashValue
}
end)
until success
end
end
local function onShutdown()
if RunService:IsStudio() then
task.wait(2)
else
local finished = Instance.new("BindableEvent")
local allPlayers = Players:GetPlayers()
local leftPlayers = #allPlayers
for _,player in ipairs(allPlayers) do
coroutine.wrap(function()
save(player, nil, true)
leftPlayers -= 1
if leftPlayers == 0 then
finished:Fire()
end
end)()
end
finished.Event:Wait()
end
end
for _,player in ipairs(Players:GetPlayers()) do
coroutine.wrap(setUp)(player)
end
Players.PlayerAdded:Connect(setUp)
Players.PlayerRemoving:Connect(save)
game:BindToClose(onShutdown)
while true do
wait(60)
for _,player in ipairs(Players:GetPlayers()) do
coroutine.wrap(save)(player, true)
end
end
?
It seems to work for me.
When going into the Explorer and changing the data/typing in a command into the command bar, make sure you are in Server mode, not Client mode.
Yeah I’m using that script
Is it because I’m saving a dictionary to the datastore?
local function playerSave(player, dontLeave, dontWait)
local playerKey = "Player_" .. player.UserId
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
local tixAmount = leaderstats.Tix.Value
local winAmount = leaderstats.Wins.Value
local areasDiscovered = areasDiscoveredTable(player)
local ventsUnlocked = unlockedVentsTable(player)
local itemsDiscovered = unlockedItemsTable(player)
local success
repeat
if not dontWait then
waitForRequestBudget()
end
success = pcall(playerData.UpdateAsync, playerData, playerKey, function()
return {
SessionLock = dontLeave and os.time() or nil,
Tix = tixAmount,
Wins = winAmount,
Items = itemsDiscovered,
Areas = areasDiscovered,
Vents = ventsUnlocked,
}
end)
until success
end
end
I replaced the playerSave with this
I just tried repeating the loop until data was not nil, and it kept its oldData.SessionLock at 1648759648
Also it seems the if ret == “Wait” then line never runs
I’m pretty sure what’s happening is success is always returning true regardless if it returns the data or not, making data always nil for some reason
Edit: Found the problem, for some reason my SessionLock in the datastore was set to a number instead of false, so when I joined it would break the data loading process
Does the data store name have to be “Name”?
No, it can be any string.
Keep in mind that if you’ve already set it to a string and now change it to another string, it will be like a new DataStore so it won’t have the data from your old one.