Hi, I made a simple datastore to save how long the player has been ingame for.
Is there any improvements I could do? I am learning how to do datastores interested to know if there are any improvements I can make.
local players = game:GetService("Players")
local dataStore = game:GetService("DataStoreService")
local Data = dataStore:GetDataStore("Data")
game.Players.PlayerAdded:Connect(function(plr)
local folder = Instance.new("Folder")
folder.Name = "Datafolder"
folder.Parent = plr
local valuecreated = Instance.new("NumberValue")
valuecreated.Name = "value"
valuecreated.Parent = plr.Datafolder
valuecreated.Value = Data:GetAsync(plr.UserId) or false
Data:SetAsync(plr.UserId, valuecreated.Value)
game.Players.PlayerRemoving:Connect(function()
Data:SetAsync(plr.UserId, valuecreated.Value)
end)
end)
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("Data")
Players.PlayerAdded:Connect(function(Player)
local leaderstats = Instance.new("Folder", Player)
leaderstats .Name = "leaderstats"
leaderstats .Parent = Player
local timePlayed = Instance.new("NumberValue", leaderstats)
timePlayed.Name = "TimePlayed"
timePlayed.Value = 0
local data
local success, err = pcall(function()
data = DataStore:GetAsync(Player.UserId.."_timePlayed")
end)
if data ~= nil then
timePlayed.Value = data[1]
end
end)
Players.PlayerRemoving:Connect(function(Player)
local leaderstats = Player.leaderstats
local Data = {}
Data[1] = leaderstats.TimePlayed.Value
local success, err = pcall(function()
DataStore:SetAsync(Player.UserId.."_timePlayed", Data)
end)
if not success then warn(err) end
end)
--Don't forget to turn on Enable Studio Access to API Services in Game Setting > Security
I tried to use UpdateAsync() but it doesn’t save them. I changed all the SetAsync() to UpdateAsync(). Also do I need to change the GetAsync() in my script to UpdateAsync() too??
In most cases it’s fine to use SetAsync. UpdateAsync is really only necessary when projects scale large, but if you want to use UpdateAsync anyway, it’s setup different from SetAsync. UpdateAsync takes the Key as the first param and a callback for the second param. Official documentation here:
This won’t work for me because I disabled the leaderstats in my game. I did try to change the code
--local leaderstats = Player.leaderstats removed this
local Data = {}
Data[1] = Player.Folder.TimePlayed.Value--I changed the name of the folder to folder
I tried to improve my script using your suggestions sadly it doesn’t work I don’t know if I did it right though.
local players = game:GetService("Players")
local dataStore = game:GetService("DataStoreService")
local Data = dataStore:GetDataStore("Data")
game.Players.PlayerAdded:Connect(function(plr)
local folder = Instance.new("Folder")
folder.Name = "Datafolder"
folder.Parent = plr
local valuecreated = Instance.new("NumberValue")
valuecreated.Name = "value"
valuecreated.Parent = plr.Datafolder
local data
local success, err = pcall(function()--this what you mean by wrap in pcall??
data = Data:GetAsync(plr.UserId, valuecreated.Value)
if data ~= nil then
Data:SetAsync(plr.UserId, valuecreated.Value)--Should I change SetAsync to UpdateAsync here
Data:SetAsync(plr.UserId, valuecreated.Value)
game.Players.PlayerRemoving:Connect(function()
Data:SetAsync(plr.UserId, valuecreated.Value)
end)
end
end)
end)
If I’m being honest you probably should just use tables a modulescript that returns data and saves data
Also I’m not too sure how safe storing data in a player’s player object is
Your datastore isn’t the best to store data securely here is an example of mine.
I think mine is a good datastore.
local DSS = game:GetService("DataStoreService")
local CashData = DSS:GetDataStore("CashData")
local GemsData = DSS:GetDataStore("GemsData")
game.Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder", player)
leaderstats.Name = "leaderstats"
local gemstats = Instance.new("Folder", player)
gemstats.Name = "gems"
local cash = Instance.new("IntValue", leaderstats)
cash.Name = "Cash"
local gems = Instance.new("IntValue", gemstats)
gems.Name = "Gems"
local playerId = "Player_"..player.UserId
local cashdata
local gemsdata
local success, errormessage = pcall(function()
cashdata = CashData:GetAsync(playerId)
gemsdata = GemsData:GetAsync(playerId)
end)
if success then
cash.Value = cashdata
gems.Value = gemsdata
end
end)
game.Players.PlayerRemoving:Connect(function(player)
local playerId = "Player_"..player.UserId
local cashdata = player.leaderstats.Cash.Value
local gemsdata = player.gems.Gems.Value
local success, errormessage = pcall(function()
CashData:SetAsync(playerId, cashdata)
GemsData:SetAsync(playerId, gemsdata)
end)
if success then
print("Saved")
else
print("There was an error")
warn(errormessage)
end
end)
I am going to start trying this right now. Quick question though if I have a lot of datastores and I am putting them on this new script. Would I receive and error about too many datastores requests. Because how I was doing it before I was using separate scripts
Without game:BindToClose() this can cause data loss as sometimes the server shuts down and doesn’t fire playerRemoving due to errors. The function holds the server open and you can save data for each player left. You can read the documentation for more info.
If many people are joining and leaving in a 1 minute frame and you have many DataStores, then it could error. You can just pack them all into a table then save to 1 DataStore (they can save tables)
game.Players.PlayerRemoving:Connect(function(player)
local playerId = "Player_"..player.UserId
local cashdata = player.leaderstats.Cash.Value
local gemsdata = player.gems.Gems.Value
local success, errormessage = pcall(function()
CashData:SetAsync(playerId, cashdata)
GemsData:SetAsync(playerId, gemsdata)
end)
if success then
print("Saved")
else
print("There was an error")
warn(errormessage)
end
game:BindToClose(function()
local playerId = "Player_"..player.UserId
local cashdata = player.leaderstats.Cash.Value
local gemsdata = player.gems.Gems.Value
local success, errormessage = pcall(function()
CashData:SetAsync(playerId, cashdata)
GemsData:SetAsync(playerId, gemsdata)
end)
if success then
print("Saved")
else
print("There was an error")
warn(errormessage)
end
end)
end)
As I said above, playerRemoving can sometimes not fire when server shuts down, so it is not the best idea. You should put it outside as a separate function then loop through each player left
game:BindToClose(function()
for i, player in pairs(game.Players:GetPlayers()) do
-- save data for player
end
end)
Ok, I made it a complete separate function, so like this?
game:BindToClose(function()
for i, player in pairs(game.Players:GetPlayers()) do
local playerId = "Player_"..player.UserId
local cashdata = player.leaderstats.Cash.Value
local gemsdata = player.gems.Gems.Value
local success, errormessage = pcall(function()
CashData:SetAsync(playerId, cashdata)
GemsData:SetAsync(playerId, gemsdata)
end)
if success then
print("Saved")
else
print("There was an error")
warn(errormessage)
end
end
end)
You can add a table containing the data you want to save, then load them into a playerTable.
ocal playertabledata = {}
local template = {
["value"] = false
}
--variables
local DataStoreService = game:GetService("DataStoreService")
local Data = game:GetDataStore("insertnamehere")
local function CheckData(async, dataname) -- replace with default value if not found data
if async == nil then
return dataname
elseif async ~= nil then
return async
end
end
local function AddNewTemplateData(player) -- updates player data with latest values
for i1,v1 in pairs(template) do
local available = false
for i2, v2 in pairs(playertabledata[player.Name]) do
if i2 == i1 then
available = true
end
end
if available == false then
playertabledata[player.Name][i1] = template[i1]
end
end
end
After that, we create functions to update values to table.
local function AddDataToTable(player) -- (save data)
if not playertabledata[player.Name] then playertabledata[player.Name] = template end -- adds playerdata to table if not found
for i,v in pairs(playertabledata[player.Name]) do -- loops through each data
playertabledata[player.Name][i] = player.leaderstats[i].Value
end
end
local function RetrieveDataFromTable(player) -- load data
if not playertabledata[player.Name] then playertabledata[player.Name] = template end -- adds playerdata to table if not found
for i,v in pairs(playertabledata[player.Name]) do -- loops through each data
player.leaderstats[i].Value = playertabledata[player.Name][i]
end
end
Then just do the same thing as normal, but with tables
game.Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local value = Instance.new("BoolValue")
value.Name = "value"
value.Parent = leaderstats
local playerId = "Player_"..player.UserId
local data1
local success, errorMessage = pcall(function()
data1 = Data:GetAsync(playerId)
end)
if success then
local success1, errorMessage1 = pcall(function()
playertabledata[player.Name] = template
playertabledata[player.Name] = CheckData(data1, template)
if data1 then
RetrieveDataFromTable(player)
end
end)
if success1 then
print("Loaded")
else
print("There was an error")
warn(errorMessage1)
end
else
print("There was an error")
warn(errorMessage)
end
end)
game.Players.PlayerRemoving:Connect(function(player)
local playerId = "Player_"..player.UserId
local data1
local success, errorMessage = pcall(function()
AddDataToTable(player)
data1 = Data:SetAsync(player.UserId, playertabledata[player.Name])
end)
if success then
print("Saved")
else
print("There was an error")
warn(errorMessage)
end
end)
game:BindToClose(function()
for i,player in pairs(game.Players:GetPlayers()) do
local playerId = "Player_"..player.UserId
local data1
local success, errorMessage = pcall(function()
AddDataToTable(player)
data1 = Data:SetAsync(player.UserId, playertabledata[player.Name])
end)
if success then
print("Saved")
else
print("There was an error")
warn(errorMessage)
end
end
end)
It is kinda long though, tell me if you need any explanations. (this is pretty similar to my game’s DataStore)
I think I did something wrong I got this error GetDataStore is not a valid member of DataModel “Game” - Server - dats:22
Also do I need to put this in there?
local CashData = DSS:GetDataStore(“CashData”)
local GemsData = DSS:GetDataStore(“GemsData”)
for each piece of data I am saving?
game.Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder", player)
leaderstats.Name = "leaderstats"
local gemstats = Instance.new("Folder", player)
gemstats.Name = "gems"
local cash = Instance.new("IntValue", leaderstats)
cash.Name = "Cash"
local gems = Instance.new("IntValue", gemstats)
gems.Name = "Gems"
local playertabledata = {}
local template = {
["Gems"] = "",
["Cash"] = ""
}
--variables
local DataStoreService = game:GetService("DataStoreService")
local Data = game:GetDataStore("gamedata")
local function CheckData(async, dataname) -- replace with default value if not found data
if async == nil then
return dataname
elseif async ~= nil then
return async
end
end
local function AddNewTemplateData(player) -- updates player data with latest values
for i1,v1 in pairs(template) do
local available = false
for i2, v2 in pairs(playertabledata[player.Name]) do
if i2 == i1 then
available = true
end
end
if available == false then
playertabledata[player.Name][i1] = template[i1]
end
end
end
local function AddDataToTable(player) -- (save data)
if not playertabledata[player.Name] then playertabledata[player.Name] = template end -- adds playerdata to table if not found
for i,v in pairs(playertabledata[player.Name]) do -- loops through each data
playertabledata[player.Name][i] = player.leaderstats[i].Value
end
end
local function RetrieveDataFromTable(player) -- load data
if not playertabledata[player.Name] then playertabledata[player.Name] = template end -- adds playerdata to table if not found
for i,v in pairs(playertabledata[player.Name]) do -- loops through each data
player.leaderstats[i].Value = playertabledata[player.Name][i]
end
end
local playerId = "Player_"..player.UserId
local cashdata
local gemsdata
local success, errormessage = pcall(function()
cashdata = Data:GetAsync(playerId)
gemsdata = Data:GetAsync(playerId)
end)
if success then
cash.Value = cashdata
gems.Value = gemsdata
end
end)
game.Players.PlayerRemoving:Connect(function(player)
local playerId = "Player_"..player.UserId
local cashdata = player.leaderstats.Cash.Value
local gemsdata = player.gems.Gems.Value
local success, errormessage = pcall(function()
cashdata:SetAsync(playerId, cashdata)
gemsdata:SetAsync(playerId, gemsdata)
end)
if success then
print("Saved")
else
print("There was an error")
warn(errormessage)
end
end)
game:BindToClose(function()
for i, player in pairs(game.Players:GetPlayers()) do
local playerId = "Player_"..player.UserId
local cashdata = player.leaderstats.Cash.Value
local gemsdata = player.gems.Gems.Value
local success, errormessage = pcall(function()
cashdata:SetAsync(playerId, cashdata)
gemsdata:SetAsync(playerId, gemsdata)
end)
if success then
print("Saved")
else
print("There was an error")
warn(errormessage)
end
end
end)