I am still pretty new to roblox scripting and am having trouble saving a table for my game. I am currently working on the tower save system for my tower defense game. Here is the code:
local DataStore = game:GetService("DataStoreService"):GetDataStore("Tower2")
local function onPlayerJoin(player)
local leaderstats = Instance.new("Folder") --Sets up leaderstats folder
leaderstats.Name = "Tower"
leaderstats.Parent = player
local Towers = {}
local PlayerData = DataStore:GetAsync(player.UserId)
print(PlayerData)
if PlayerData ~= nil then
print("Found Data")
for _, i in pairs(PlayerData) do
local TVal = Instance.new("StringValue")
TVal.Name = PlayerData[i]
TVal.Parent = leaderstats
TVal.Value = PlayerData[i]
end
else
warn("Could not find local save")
local TVal = Instance.new("StringValue")
TVal.Name = "Soldier"
TVal.Parent = leaderstats
TVal.Value = "Soldier"
end
end
local function create_table(player)
local player_stats = {}
for _, stat in pairs(player.Tower:GetChildren()) do
player_stats[stat.Name] = stat.Value
end
return player_stats
end
local function onPlayerExit(player) --Runs when players exit
print("Exited")
local player_stats = create_table(player)
local success, err = pcall(function()
local playerUserId = "Player_" .. player.UserId
DataStore:SetAsync(playerUserId, player_stats) --Saves player data
print("Saved")
print(player_stats)
end)
if not success then
warn('Could not save data!')
end
end
game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)
Inside the player is a folder with a value for each tower that they currently own. The script creates these values upon joining the game and is supposed to save them when the player leaves. The problem is that the script is not actually saving the players data. There are no errors in the output and the datastore prints nil when joining. I have tried to fix this for the last several hours but nothing is working. Can anyone give me some advice on how I could make this work?
first of all, the player keys are different here. You save it to "Player_"..player.UserId, but try to get it from just player.UserId
Make sure to use the same key. The saving should work perfectly fine. It’s just the getting. Also,
The “Name” here should be _ and the Value should be i, but It would probably just be better to change the for variables. So, I’ll do that for you.
for i, v in pairs(PlayerData) do
local TVal = Instance.new("StringValue")
TVal.Name = i
TVal.Parent = leaderstats
TVal.Value = v
end
@Kaid3n22 is right, also you should wrap those data store functions in a pcall function, because data stores can fail time to time.
local PlayerData
local success, err = pcall(function()
local PlayerData = DataStore:GetAsync("Player_"..player.UserId)
end)
if PlayerData then
-- set your data here
end
Edit: I noticed that you used SetAsync, which is not good and I recommend using UpdateAsync.
Thanks for the help I think it worked, It is not loading the saved data which I’m assuming is just because the server is shutting down before the game can save
1- Are you ever using the game:BindToClose method?
local playersSaving = {}
local bindToCloseThreads = {}
local function onPlayerExit(player) --Runs when players exit
if not playersSaving[player] then
playersSaving[player] = true
print("Exited")
local player_stats = create_table(player)
local success, err = pcall(function()
local playerUserId = "Player_" .. player.UserId
DataStore:SetAsync(playerUserId, player_stats) --Saves player data
print("Saved")
print(player_stats)
end)
if not success then
warn('Could not save data!')
end
if bindToCloseThreads[player] ~= nil then
bindToCloseThreads[player] = true
end
playersSaving[player] = nil
end
end
local function allSaved()
for i,v in pairs(playersSaving) do
if not v then
return v
end
end
end
game:BindToClose(function()
for i,player in pairs(game:GetService('Players'):GetPlayers()) do
coroutine.wrap(onPlayerExit)(player)
bindToCloseThreads[player] = false
end
repeat game:GetService('RunService').Heartbeat:Wait() until allSaved()
end)
2- You should check if the :GetAsync request failed, if it does fail, it’s going to set the player’s data to nil.
local playerData
local success, errorMessage = pcall(function()
playerData = DataStore:GetAsync(player.UserId)
end)
if success then
if playerData ~= nil then
for i,value in pairs(PlayerData) do
local TVal = Instance.new("StringValue")
TVal.Name = i
TVal.Parent = leaderstats
TVal.Value = value
end
else -- if it's blank but the request was still successful
local TVal = Instance.new("StringValue")
TVal.Name = "Soldier"
TVal.Parent = leaderstats
TVal.Value = "Soldier"
end
else -- if the request failed
warn('GetAsync failed for',player,': ',tostring(errorMessage)
end
3- You’re writing and getting from different keys as someone mentioned already. You should only write and get from the same key.
I have revised the code but it is still not able to find the saved data
revised code:
local DataStore = game:GetService("DataStoreService"):GetDataStore("Tower2")
local playersSaving = {}
local bindToCloseThreads = {}
local function onPlayerJoin(player)
local leaderstats = Instance.new("Folder") --Sets up leaderstats folder
leaderstats.Name = "Tower"
leaderstats.Parent = player
local Towers = {}
local playerUserId = "Player_" .. player.UserId
local playerData
local success, errorMessage = pcall(function()
playerData = DataStore:GetAsync(player.UserId)
end)
if success then
if playerData then
print("Found Data")
for i, v in pairs(playerData) do
local TVal = Instance.new("StringValue")
TVal.Name = i
TVal.Parent = leaderstats
TVal.Value = v
end
else
warn("Could not find local save")
local TVal = Instance.new("StringValue")
TVal.Name = "Soldier"
TVal.Parent = leaderstats
TVal.Value = "Soldier"
end
else
warn('GetAsync failed for',player,': ',tostring(errorMessage))
end
end
local function create_table(player)
local player_stats = {}
for _, stat in pairs(player.Tower:GetChildren()) do
player_stats[stat.Name] = stat.Value
end
return player_stats
end
local function onPlayerExit(player)
if not playersSaving[player] then
playersSaving[player] = true
print("Exited")
local player_stats = create_table(player)
local success, err = pcall(function()
DataStore:SetAsync(player.UserId, player_stats)
print("Saved")
print(player_stats)
end)
if not success then
warn('Could not save data!')
end
if bindToCloseThreads[player] ~= nil then
bindToCloseThreads[player] = true
end
playersSaving[player] = nil
end
end
game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)
ok so now it is loading the data but isnt saving it
Code:
local DataStore = game:GetService("DataStoreService"):GetDataStore("Tower2")
local playersSaving = {}
local bindToCloseThreads = {}
local function onPlayerJoin(player)
local leaderstats = Instance.new("Folder") --Sets up leaderstats folder
leaderstats.Name = "Tower"
leaderstats.Parent = player
local Towers = {}
local playerUserId = "Player_" .. player.UserId
local playerData
local success, errorMessage = pcall(function()
playerData = DataStore:GetAsync(player.UserId)
end)
if success then
if playerData then
print("Found Data")
for i, v in pairs(playerData) do
local TVal = Instance.new("StringValue")
TVal.Name = i
TVal.Parent = leaderstats
TVal.Value = v
end
else
warn("Could not find local save")
local TVal = Instance.new("StringValue")
TVal.Name = "Soldier"
TVal.Parent = leaderstats
TVal.Value = "Soldier"
end
else
warn('GetAsync failed for',player,': ',tostring(errorMessage))
end
end
local function create_table(player)
local player_stats = {}
for _, stat in pairs(player.Tower:GetChildren()) do
table.insert(player_stats, stat.Value)
end
return player_stats
end
local function onPlayerExit(player)
if not playersSaving[player] then
playersSaving[player] = true
print("Exited")
local player_stats = create_table(player)
local success, err = pcall(function()
DataStore:SetAsync(player.UserId, player_stats)
print("Saved")
print(player_stats)
end)
if not success then
warn('Could not save data!')
end
if bindToCloseThreads[player] ~= nil then
bindToCloseThreads[player] = true
end
playersSaving[player] = nil
end
end
game:BindToClose(function()
for i,v in pairs(game.Players:GetPlayers()) do
onPlayerExit(v)
end
end)
game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)
ok i dont know what i did but now it loaded the number 1 with a vale of Soldier. It should be loading “Soldier” with a value of soldier so I don’t know how this happened
Code:
local DataStore = game:GetService("DataStoreService"):GetDataStore("Tower2")
local playersSaving = {}
local bindToCloseThreads = {}
local function onPlayerJoin(player)
local leaderstats = Instance.new("Folder") --Sets up leaderstats folder
leaderstats.Name = "Tower"
leaderstats.Parent = player
local Towers = {}
local playerUserId = "Player_" .. player.UserId
local playerData
local success, errorMessage = pcall(function()
playerData = DataStore:GetAsync(player.UserId)
end)
if success then
if playerData then
print("Found Data")
for i, v in pairs(playerData) do
local TVal = Instance.new("StringValue")
TVal.Name = i
TVal.Parent = leaderstats
TVal.Value = v
end
else
warn("Could not find local save")
local TVal = Instance.new("StringValue")
TVal.Name = "Soldier"
TVal.Parent = leaderstats
TVal.Value = "Soldier"
end
else
warn('GetAsync failed for',player,': ',tostring(errorMessage))
end
end
local function create_table(player)
local player_stats = {}
for _, stat in pairs(player.Tower:GetChildren()) do
table.insert(player_stats, stat.Value)
end
return player_stats
end
local function onPlayerExit(player)
if not playersSaving[player] then
playersSaving[player] = true
print("Exited")
local player_stats = create_table(player)
local success, err = pcall(function()
DataStore:SetAsync(player.UserId, player_stats)
print("Saved")
print(player_stats)
end)
if not success then
warn('Could not save data!')
end
if bindToCloseThreads[player] ~= nil then
bindToCloseThreads[player] = true
end
playersSaving[player] = nil
end
end
game:BindToClose(function()
for i,v in pairs(game.Players:GetPlayers()) do
onPlayerExit(v)
end
end)
game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)```
local function create_table(player)
local player_stats = {}
for _, stat in pairs(player.Tower:GetChildren()) do
table.insert(player_stats, stat.Value)
print(stat.Value)
end
return player_stats
end
Is only saving the first value in the folder. I don’t know why this is happening. As far as I can tell this should be saving all the values and not just one value. Can someone please tell me how I could fix this?
Here is the rest of the script:
local DataStore = game:GetService("DataStoreService"):GetDataStore("Tower2")
local playersSaving = {}
local bindToCloseThreads = {}
local function onPlayerJoin(player)
local leaderstats = Instance.new("Folder") --Sets up leaderstats folder
leaderstats.Name = "Tower"
leaderstats.Parent = player
local Towers = {}
local playerUserId = "Player_" .. player.UserId
local playerData
local success, errorMessage = pcall(function()
playerData = DataStore:GetAsync(player.UserId)
end)
if success then
if playerData then
print("Found Data")
for i, v in pairs(playerData) do
local TVal = Instance.new("StringValue")
TVal.Name = v
TVal.Parent = leaderstats
TVal.Value = v
end
else
warn("Could not find local save")
local TVal = Instance.new("StringValue")
TVal.Name = "Soldier"
TVal.Parent = leaderstats
TVal.Value = "Soldier"
end
else
warn('GetAsync failed for',player,': ',tostring(errorMessage))
end
end
local function create_table(player)
local player_stats = {}
for _, stat in pairs(player.Tower:GetChildren()) do
table.insert(player_stats, stat.Value)
print(stat.Value)
end
return player_stats
end
local function onPlayerExit(player)
if not playersSaving[player] then
playersSaving[player] = true
print("Exited")
local player_stats = create_table(player)
local success, err = pcall(function()
DataStore:SetAsync(player.UserId, player_stats)
print("Saved")
print(player_stats)
end)
if not success then
warn('Could not save data!')
end
if bindToCloseThreads[player] ~= nil then
bindToCloseThreads[player] = true
end
playersSaving[player] = nil
end
end
game:BindToClose(function()
for i,v in pairs(game.Players:GetPlayers()) do
onPlayerExit(v)
end
end)
game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)
OHHHHHHH im an idiot. I was trying to give the player values through the client while the save script was server sided. It automatically gives the player the soldier tower so it could only save that since the rest was client sided.
I don’t know what i did but it has now saved the number 0 as a tower and also fills the datastore request queue whenever I leave
here is the code:
local DataStore = game:GetService("DataStoreService"):GetDataStore("Tower2")
local playersSaving = {}
local bindToCloseThreads = {}
local function onPlayerJoin(player)
local leaderstats = Instance.new("Folder") --Sets up leaderstats folder
leaderstats.Name = "Tower"
leaderstats.Parent = player
local Towers = {}
local playerUserId = "Player_" .. player.UserId
local playerData
local success, errorMessage = pcall(function()
playerData = DataStore:GetAsync(player.UserId)
end)
if success then
if playerData then
print("Found Data")
for i, v in pairs(playerData) do
local TVal = Instance.new("StringValue")
TVal.Name = v
TVal.Parent = leaderstats
TVal.Value = v
end
else
warn("Could not find local save")
local TVal = Instance.new("StringValue")
TVal.Name = "Soldier"
TVal.Parent = leaderstats
TVal.Value = "Soldier"
end
else
warn('GetAsync failed for',player,': ',tostring(errorMessage))
end
end
local function create_table(player)
local player_stats = {}
for _, stat in pairs(player.Tower:GetChildren()) do
player_stats[stat.name] = stat.Value
print(stat.Value)
end
return player_stats
end
local function onPlayerExit(player)
if not playersSaving[player] then
playersSaving[player] = true
print("Exited")
local player_stats = create_table(player)
local success, err = pcall(function()
DataStore:SetAsync(player.UserId, player_stats)
print("Saved")
print(player_stats)
end)
if not success then
warn('Could not save data!')
end
if bindToCloseThreads[player] ~= nil then
bindToCloseThreads[player] = true
end
playersSaving[player] = nil
end
end
game:BindToClose(function()
for i,v in pairs(game.Players:GetPlayers()) do
onPlayerExit(v)
end
end)
game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)