I think I can go ahead and instruct you on how Data Stores should work, there is an actual Documentation for it as well if you’d like to research about it but I’ll go ahead & explain the issues with your code here if that’s fine :L
The Whole Explanation & Process
Within your first lines here, you keep obtaining the DataStoreService
whenever a Player joins the game which isn’t exactly good practice, where instead you should define your datastore
& ds1
variables outside the function so that we can easily obtain it within our script:
local datastore = game:GetService("DataStoreService")--Gets the service
local ds1 = datastore:GetDataStore("DataSaver")
local AutoSaveInterval = 60
Now I went ahead and added in a new variable here, but I’ll explain that later when I get to it
Next up, please do not put the Instance.new(“Object”, Parent) within your script as it actually takes up more performance then what you’d originally expect, but do make sure to go ahead and set the .Parent
property last after changing & setting all your Properties:
local leaderstats = Instance.new("IntValue")
leaderstats.Name = "leaderstats"
leaderstats.Parent = p
local wins = Instance.new("IntValue")
wins.Name = "Wins"
wins.Parent = leaderstats
local Points = Instance.new("IntValue")
Points.Name = "Points"
Points.Value = 0
Points.Parent = leaderstats
Next up on the list, are your Data Store functions. It’s highly recommended to encase a function that may return back nil
in something that’s called a pcall() function (Or protective calls), as these ensure that your script doesn’t fully error so you can prepare for situations like lost data & such
What you’re trying to do, is assume that ds1:GetAsync/ds1:SetAsync
is fine when calling it, but it’s better to be prepared rather than not, so we’ll go ahead and add 2 functions that I included which is capable of handling DataStore functions:
local function SaveData(Plr, Data)
local success, whoops = pcall(function()
ds1:SetAsync(Plr.UserId, Data)
end)
return success
end
local function LoadData(Plr)
local Data = nil
local success, whoops = pcall(function()
Data = ds1:GetAsync(Plr.UserId)
end)
if success then
print("Loaded data successfully!")
else
warn("There was an error retrieving the Player's Data!", whoops)
return false
end
return Data
end
In our scenario here, we want to encase our data stores within pcall
functions so that they don’t throw back an error in our Console, and we can add a response whether or not if the data has been loaded or not
It isn’t necessary to have these functions exactly, but it’s definitely more organized & you can easily reference it this way, and both of these functions will return
something back as a result
In case you don’t know what that is, return
is simply just returning a value back from where we originally called our function from (With our provided parameters), it’s kinda like a Yard Sale if you think about it here:
You want to pay for something within the Yard, and you get the specified item back in return
- Paying: $50.00 (
SaveData(PlrObject, DataTable)
)
- The Result of Paying: Teddy Bear (
return DataResult
)
Next up in our PlayerAdded
Event, we want to call our LoadData()
function that we just created, and reference it with a Player object so that we know what object we’re specifying for exactly:
local leaderstats = Instance.new("IntValue")
leaderstats.Name = "leaderstats"
leaderstats.Parent = p
local wins = Instance.new("IntValue")
wins.Name = "Wins"
wins.Parent = leaderstats
local Points = Instance.new("IntValue")
Points.Name = "Points"
Points.Parent = leaderstats
local Data = LoadData(p)
if Data then
Points.Value = Data.Points
wins.Value = Data.Wins
else
Points.Value = 0
wins.Value = 0
end
And we’ll go ahead to focus on each part specifically on how our LoadData
function will work:
local function LoadData(Plr) -- This is our Player that we tranferred over to ( LoadData(p) )
local Data = nil -- We create a variable for our Data, so that we can check if it's equal to `nil` or not
local success, whoops = pcall(function() -- We encase this in a pcall function here
Data = ds1:GetAsync(Plr.UserId) -- Call GetAsync()
end)
if success then -- "success" will return back true or false if the `GetAsync()` function properly worked, or if it returned back an error
print("Loaded data successfully!")
else
warn("There was an error retrieving the Player's Data!", whoops)
return false -- An error occured, whoops! We want to return this value back as "false" and this will actually stop this function so that "return Data" will not run!
end
return Data -- We found data, now we can return this back!
end
Printing is 1 of the most important debugging features to have, make sure to use it we whenever you can!
Moving on though, I highly suggest not to do this, especially since you have a loop going on that increases the Point.Value
by +1 per second, because this will result in DataStore requests queueing up your ds1
, which’ll start to throttle your Data Stores and you should do them not that frequently, & most effective when possible
And now comes the next section here, we want to create another loop (After your v.Touched
loop) that’ll auto-save every Player’s individual data depending how often we want it to save
Remember that AutoSaveInterval
variable we defined earlier ago? Well, it’s time to use that which’ll estimate our Auto Save rate:
-- Now here, we want to implement a loop that'll autosave our Data every 60 seconds:
while true do
task.wait(AutoSaveInterval)
for _, Plr in pairs(game.Players:GetPlayers()) do
local leaderstats = Plr:WaitForChild("leaderstats")
local PlrWins = leaderstats:WaitForChild("Wins")
local PlrPoints = leaderstats:WaitForChild("Points")
local DataTable = {
Wins = PlrWins.Value,
Points = PlrPoints.Value,
}
local SaveResult = SaveData(Plr, DataTable)
if SaveResult == true then
print(Plr.Name.."'s Data has been saved successfully!")
end
end
end
Now what we’re doing here, is obtaining all our Players using a function called GetPlayers() through another loop, converting our Data into a table
which possesses the ability to store multiple values into 1 variable here, and since we define local DataTable
inside our secondary loop (Not our first one, which is while true do
!), we call our SaveData()
function and create it into a variable (SaveResult), so we have to specify our 2 parameters which are:
- The Player Object
- The Data Table
Now SaveResult
can return back as a bool value (Or true/false), and we can check if SaveResult == true
to respond back with a success print if it saved!
Overall, our script should look like this in its full entirety!
I definitely hope though that you’ve learned something from all of this, and that you can take note on what the best practices are!