Hi, so I’ve made a datastore and not really too sure how I can improve apon this code. (things such as data prevention, etc) Let me know how I could improve this, thanks!
-- Variables
local DataService = game:GetService("DataStoreService")
local DataStore = DataService:GetDataStore("data_01")
local camera = workspace.CurrentCamera
local cpFolder = game.Workspace:WaitForChild("Checkpoints")
-- PlayerAdded Function
game.Players.PlayerAdded:Connect(function(player)
local key = "player-" .. player.UserId
local GetSave = DataStore:GetAsync(key) -- check for existing data
local leaderstats = Instance.new("Folder", player)
leaderstats.Name = "leaderstats"
local hidden = Instance.new("Folder", player)
hidden.Name = "hidden"
local checkpoint = Instance.new("IntValue", leaderstats)
checkpoint.Name = "Stage"
local spawnpoint = Instance.new("IntValue", hidden)
spawnpoint.Name = "SpawnPoint"
if GetSave then
checkpoint.Value = GetSave
print("Data Loaded For " .. player.Name)
else
checkpoint.Value = 1
print("New Data Created For " .. player.Name)
end
player.hidden.SpawnPoint.Value = player.leaderstats.Stage.Value
local character = player.Character or player.CharacterAdded:Wait()
local respawn2 = cpFolder:WaitForChild(player.leaderstats.Stage.Value)
character:WaitForChild("HumanoidRootPart").CFrame = respawn2.CFrame + Vector3.new(0,3,0)
-- CharacterAdded Function
player.CharacterAdded:Connect(function(character)
repeat wait() until workspace:FindFirstChild(character.Name)
local player = game.Players:GetPlayerFromCharacter(character)
local respawn = cpFolder[player.hidden.SpawnPoint.Value]
character.HumanoidRootPart.CFrame = respawn.CFrame + Vector3.new(0,3,0)
end)
end)
-- PlayerRemoving Event
game.Players.PlayerRemoving:Connect(function(player)
local key = "player-" .. player.UserId
DataStore:SetAsync(key, player.leaderstats.Stage.Value)
print("Data Successfully Saved For " .. player.Name)
end)
One way you can improve your script is by using pcalls. Currently, if your datastore calls were to fail, the player’s data would be lost and overwritten.
Here is a topic about pcalls:
Instead of having a repeat - until loop, you can use Instance:WaitForChild().
repeat wait() until workspace:FindFirstChild(character.Name)
I have a slight question on datastore failure, currently when I test my game in studio and I progress a level (testing purposes) usually It won’t actually save my data at that specific level I’ve completed and it’ll set me back a stage when I test again. In-game it works perfectly fine though. Is this an issue on Roblox’s end or mine?
Awesome, I read the API and adjusted some things. Does this look better for it saving in studio? (added BindToClose() event from the API)
-- Variables
local DataService = game:GetService("DataStoreService")
local DataStore = DataService:GetDataStore("data_01")
local camera = workspace.CurrentCamera
local cpFolder = game.Workspace:WaitForChild("Checkpoints")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local playerData = {
-- [UserId] = data
}
game:BindToClose(function()
-- if the current session is studio, do nothing
if RunService:IsStudio() then
return
end
print("saving player data")
-- go through all players, saving their data
local players = Players:GetPlayers()
for _, player in pairs(players) do
local userId = player.UserId
local data = playerData[userId]
if data then
-- wrap in pcall to handle any errors
local success, result = pcall(function()
-- SetAsync yields so will stall shutdown
DataStore:SetAsync(userId, data)
end)
if not success then
warn(result)
end
end
end
print("completed saving player data")
end)
-- PlayerAdded Function
game.Players.PlayerAdded:Connect(function(player)
local key = "player-" .. player.UserId
local GetSave = DataStore:GetAsync(key) -- check for existing data
local leaderstats = Instance.new("Folder", player)
leaderstats.Name = "leaderstats"
local hidden = Instance.new("Folder", player)
hidden.Name = "hidden"
local checkpoint = Instance.new("IntValue", leaderstats)
checkpoint.Name = "Stage"
local spawnpoint = Instance.new("IntValue", hidden)
spawnpoint.Name = "SpawnPoint"
if GetSave then
checkpoint.Value = GetSave
print("Data Loaded For " .. player.Name)
else
checkpoint.Value = 1
print("New Data Created For " .. player.Name)
end
player.hidden.SpawnPoint.Value = player.leaderstats.Stage.Value
local character = player.Character or player.CharacterAdded:Wait()
local respawn2 = cpFolder:WaitForChild(player.leaderstats.Stage.Value)
character:WaitForChild("HumanoidRootPart").CFrame = respawn2.CFrame + Vector3.new(0,3,0)
-- CharacterAdded Function
player.CharacterAdded:Connect(function(character)
repeat wait() until workspace:FindFirstChild(character.Name)
local player = game.Players:GetPlayerFromCharacter(character)
local respawn = cpFolder[player.hidden.SpawnPoint.Value]
character.HumanoidRootPart.CFrame = respawn.CFrame + Vector3.new(0,3,0)
end)
end)
-- PlayerRemoving Event
game.Players.PlayerRemoving:Connect(function(player)
local key = "player-" .. player.UserId
DataStore:SetAsync(key, player.leaderstats.Stage.Value)
print("Data Successfully Saved For " .. player.Name)
end)
Wait so the BindToClose() function works or do I have to switch the supposed key and it’ll work? I’ll look into coroutines and I’ve read about retry systems so I’ll add that but here’s my updated script:
Added a p-call yesterday night near the bottom.
-- Variables
local DataService = game:GetService("DataStoreService")
local DataStore = DataService:GetDataStore("data_01")
local camera = workspace.CurrentCamera
local cpFolder = game.Workspace:WaitForChild("Checkpoints")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local playerData = {
-- [UserId] = data
}
game:BindToClose(function()
-- if the current session is studio, do nothing
if RunService:IsStudio() then
print("WORKING")
return
end
print("saving player data")
-- go through all players, saving their data
local players = Players:GetPlayers()
for _, player in pairs(players) do
local userId = player.UserId
local data = playerData[userId]
if data then
-- wrap in pcall to handle any errors
local success, result = pcall(function()
-- SetAsync yields so will stall shutdown
DataStore:SetAsync(userId, data)
end)
if not success then
warn(result)
end
end
end
print("completed saving player data")
end)
-- PlayerAdded Function
game.Players.PlayerAdded:Connect(function(player)
local key = "player-" .. player.UserId
local GetSave = DataStore:GetAsync(key) -- check for existing data
local leaderstats = Instance.new("Folder", player)
leaderstats.Name = "leaderstats"
local hidden = Instance.new("Folder", player)
hidden.Name = "hidden"
local checkpoint = Instance.new("IntValue", leaderstats)
checkpoint.Name = "Stage"
local spawnpoint = Instance.new("IntValue", hidden)
spawnpoint.Name = "SpawnPoint"
if GetSave then
checkpoint.Value = GetSave
print("Data Loaded For " .. player.Name)
else
checkpoint.Value = 1
print("New Data Created For " .. player.Name)
end
player.hidden.SpawnPoint.Value = player.leaderstats.Stage.Value
local character = player.Character or player.CharacterAdded:Wait()
local respawn2 = cpFolder:WaitForChild(player.leaderstats.Stage.Value)
character:WaitForChild("HumanoidRootPart").CFrame = respawn2.CFrame + Vector3.new(0,3,0)
-- CharacterAdded Function
player.CharacterAdded:Connect(function(character)
repeat wait() until workspace:FindFirstChild(character.Name)
local player = game.Players:GetPlayerFromCharacter(character)
local respawn = cpFolder[player.hidden.SpawnPoint.Value]
character.HumanoidRootPart.CFrame = respawn.CFrame + Vector3.new(0,3,0)
end)
end)
-- PlayerRemoving Event
game.Players.PlayerRemoving:Connect(function(player)
local key = "player-" .. player.UserId
DataStore:SetAsync(key, player.leaderstats.Stage.Value)
print("Data Successfully Saved For " .. player.Name)
end)
local success, response = pcall(
DataStore.GetAsync, --1
DataStore, --2
"key" --3
)
It works, but you are using separate keys to save data.
-- BindToClose function
local userId = player.UserId
DataStore:SetAsync(userId, data)
-- PlayerAdded function
local key = "player-" .. player.UserId
local GetSave = DataStore:GetAsync(key)
-- PlayerRemoving function
local key = "player-" .. player.UserId
DataStore:SetAsync(key, player.leaderstats.Stage.Value)
Also, the call at the bottom of your script will not do anything. You have to enclose it in the actual PlayerAdded/PlayerRemoving functions.
I put the p-calls in the function and adjusted the data save keys:
-- Variables
local DataService = game:GetService("DataStoreService")
local DataStore = DataService:GetDataStore("data_01")
local camera = workspace.CurrentCamera
local cpFolder = game.Workspace:WaitForChild("Checkpoints")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local playerData = {
-- [UserId] = data
}
game:BindToClose(function()
-- if the current session is studio, do nothing
if RunService:IsStudio() then
print("WORKING")
return
end
print("saving player data")
-- go through all players, saving their data
local players = Players:GetPlayers()
for _, player in pairs(players) do
local userId = player.UserId
local key = playerData[userId]
if key then
-- wrap in pcall to handle any errors
local success, result = pcall(function()
-- SetAsync yields so will stall shutdown
DataStore:SetAsync(key, player.leaderstats.Stage.Value)
end)
if not success then
warn(result)
end
end
end
print("completed saving player data")
end)
-- PlayerAdded Function
game.Players.PlayerAdded:Connect(function(player)
local key = "player-" .. player.UserId
local GetSave = DataStore:GetAsync(key) -- check for existing data
local leaderstats = Instance.new("Folder", player)
leaderstats.Name = "leaderstats"
local hidden = Instance.new("Folder", player)
hidden.Name = "hidden"
local checkpoint = Instance.new("IntValue", leaderstats)
checkpoint.Name = "Stage"
local spawnpoint = Instance.new("IntValue", hidden)
spawnpoint.Name = "SpawnPoint"
if GetSave then
checkpoint.Value = GetSave
print("Data Loaded For " .. player.Name)
else
checkpoint.Value = 1
print("New Data Created For " .. player.Name)
end
player.hidden.SpawnPoint.Value = player.leaderstats.Stage.Value
local character = player.Character or player.CharacterAdded:Wait()
local respawn2 = cpFolder:WaitForChild(player.leaderstats.Stage.Value)
character:WaitForChild("HumanoidRootPart").CFrame = respawn2.CFrame + Vector3.new(0,3,0)
local success, response = pcall(
DataStore.GetAsync, --1
DataStore, --2
"key" --3
)
-- CharacterAdded Function
player.CharacterAdded:Connect(function(character)
repeat wait() until workspace:FindFirstChild(character.Name)
local player = game.Players:GetPlayerFromCharacter(character)
local respawn = cpFolder[player.hidden.SpawnPoint.Value]
character.HumanoidRootPart.CFrame = respawn.CFrame + Vector3.new(0,3,0)
end)
end)
-- PlayerRemoving Event
game.Players.PlayerRemoving:Connect(function(player)
local key = "player-" .. player.UserId
DataStore:SetAsync(key, player.leaderstats.Stage.Value)
print("Data Successfully Saved For " .. player.Name)
local success, response = pcall(
DataStore.GetAsync, --1
DataStore, --2
"key" --3
)
end)
-- Variables
local DataService = game:GetService("DataStoreService")
local DataStore = DataService:GetDataStore("data_01")
local camera = workspace.CurrentCamera
local cpFolder = game.Workspace:WaitForChild("Checkpoints")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local function saveData(player) -- The functions that saves data
local tableToSave = {
player.leaderstats.Stage.Value -- First value from the table
}
local success, err = pcall(function()
DataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
end)
if success then -- If the data has been saved
print("Data has been saved!")
else -- Else if the save failed
print("Data hasn't been saved!")
warn(err)
end
end
-- PlayerAdded Function
game.Players.PlayerAdded:Connect(function(player)
local key = "player-" .. player.UserId
local GetSave = DataStore:GetAsync(key) -- check for existing data
local leaderstats = Instance.new("Folder", player)
leaderstats.Name = "leaderstats"
local hidden = Instance.new("Folder", player)
hidden.Name = "hidden"
local checkpoint = Instance.new("IntValue", leaderstats)
checkpoint.Name = "Stage"
local spawnpoint = Instance.new("IntValue", hidden)
spawnpoint.Name = "SpawnPoint"
if GetSave then
checkpoint.Value = GetSave
print("Data Loaded For " .. player.Name)
else
checkpoint.Value = 1
print("New Data Created For " .. player.Name)
end
local data -- We will define the data here so we can use it later, this data is the table we saved
local success, err = pcall(function()
data = DataStore:GetAsync(player.UserId)
player.hidden.SpawnPoint.Value = player.leaderstats.Stage.Value
local character = player.Character or player.CharacterAdded:Wait()
local respawn2 = cpFolder:WaitForChild(player.leaderstats.Stage.Value)
character:WaitForChild("HumanoidRootPart").CFrame = respawn2.CFrame + Vector3.new(0,3,0)
-- CharacterAdded Function
player.CharacterAdded:Connect(function(character)
repeat wait() until workspace:FindFirstChild(character.Name)
local player = game.Players:GetPlayerFromCharacter(character)
local respawn = cpFolder[player.hidden.SpawnPoint.Value]
character.HumanoidRootPart.CFrame = respawn.CFrame + Vector3.new(0,3,0)
end)
end)
if success then -- If there were no errors and player loaded the data
checkpoint.Value = data[1] -- Set the money to the first value of the table (data)
print("Sucess!")
else -- The player didn't load in the data, and probably is a new player
print("The player has no data!") -- The default will be set to 0
end
end)
-- PlayerRemoving Event
game.Players.PlayerRemoving:Connect(function(player)
local success, err = pcall(function()
local key = "player-" .. player.UserId
DataStore:SetAsync(key, player.leaderstats.Stage.Value)
print("Data Successfully Saved For " .. player.Name)
end)
if success then
print("Data has been saved")
else
print("Data has not been saved!")
end
end)
-- BindToClose Event
game:BindToClose(function() -- When the server shuts down
for _, player in pairs(game.Players:GetPlayers()) do -- Loop through all the players
local success, err = pcall(function()
local key = "player-" .. player.UserId
DataStore:SetAsync(key, player.leaderstats.Stage.Value)
print("Data Successfully Saved For " .. player.Name)
end)
if success then
print("Data has been saved")
else
print("Data has not been saved!")
end
end
end)
Expect for the PlayerAdded function because I get this error: ServerScriptService.Datastores.MainDatastore:76: attempt to index nil with number - Server - MainDatastore:76
Deleted that, does this work though? I haven’t seen it print in the output so I’m assuming not and how would I fix that?
local function saveData(player) -- The functions that saves data
local tableToSave = {
player.leaderstats.Stage.Value -- First value from the table
}
local success, err = pcall(function()
DataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
end)
if success then -- If the data has been saved
print("Data has been saved!")
else -- Else if the save failed
print("Data hasn't been saved!")
warn(err)
end
end
You aren’t using pcalls for loading and saving data. Both of them are Async functions which send a HTTP call to an API and the response from that API can be an error.