local ProfileService = require(game.ReplicatedStorage.ProfileService)
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("TimeStats")
local service = game:GetService("MarketplaceService")
local Profiles = {}
local GamepassId = 154681682
local saveStructure = {
Donated = 0;
Raised = 0;
Minutes = 0;
}
local PlayerProfileStore = ProfileService.GetProfileStore("PlayerSaveData", saveStructure)
local function PlayerDataLoaded(player)
local profile = Profiles[player]
local folder = Instance.new("Folder")
folder.Name = "leaderstats"
folder.Parent = player
local Donated = Instance.new("IntValue")
Donated.Name = "Donated"
Donated.Value = profile.Data.Donated
Donated.Parent = folder
local Raised = Instance.new("IntValue")
Raised.Name = "Raised"
Raised.Value = profile.Data.Raised
Raised.Parent = folder
local Minutes = Instance.new("IntValue")
Minutes.Name = "Minutes"
Minutes.Value = profile.Data.Minutes
Minutes.Parent = folder
spawn(function()
local profile = Profiles[player]
if profile ~= nil then
Donated.Value = profile.Data.Donated
Raised.Value = profile.Data.Raised
Minutes.Value = profile.Data.Minutes
end
end)
end
local function PlayerAdded(player)
local profile = PlayerProfileStore:LoadProfileAsync("Player_" .. player.UserId, "ForceLoad")
if profile ~= nil then
profile:ListenToRelease(function()
Profiles[player] = nil
player:Kick("Your profile has been loaded remotely. Please rejoin.")
end)
if player:IsDescendantOf(Players) then
Profiles[player] = profile
PlayerDataLoaded(player)
else
profile:Release()
end
else
player:Kick("Unable to load saved data. Please rejoin.")
end
end
for _, player in ipairs(Players:GetPlayers()) do
spawn(function()
PlayerAdded(player)
end)
end
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(function(player)
local profile = Profiles[player]
if profile ~= nil then
profile:Release()
end
end)
local function AddRaised(userID, amount)
local player = Players:GetPlayerByUserId(userID)
if player then
local profile = Profiles[player]
profile.Data.Raised = profile.Data.Raised + amount
end
end
local function AddDonated(userID, amount)
local player = Players:GetPlayerByUserId(userID)
if player then
local profile = Profiles[player]
profile.Data.Donated = profile.Data.Donated + amount
end
end
local function MinPlayed(userID, amount)
local player = Players:GetPlayerByUserId(userID)
if player then
local profile = Profiles[player]
profile.Data.Minutes = profile.Data.Minutes + amount
end
end
game.Players.PlayerAdded:Connect(function(Player)
local Minutes = Player:WaitForChild("leaderstats"):WaitForChild("Minutes")
local Data = DataStore:GetAsync(Player.UserId) -- Get Data
if type(Data) ~= "table" then
Data = nil
end
local incrementValue = 1 -- value when adding points
if (service:UserOwnsGamePassAsync(Player.UserId, GamepassId)) then -- 2x gamepass
incrementValue = 2
end
--[{ TIME GIVERS }]--
coroutine.resume(coroutine.create(function() -- gives 1 point every minute
while true do
wait(60) -- every minute
Minutes.Value = Minutes.Value + incrementValue -- adds points based off of the incrementValue
end
end))
end)
game.Players.PlayerRemoving:Connect(function(Player)
DataStore:SetAsync(Player.UserId, Player.leaderstats.Minutes.Value)
end)
game.ReplicatedStorage.Events.AddValue.Event:Connect(function(userID, amount, stat)
if stat == "Raised" then
AddRaised(userID, amount)
elseif stat == "Donated" then
AddDonated(userID, amount)
elseif stat == "Minutes" then
MinPlayed(userID, amount)
end
end)
local GetDataEvent = game.ReplicatedStorage.Events.GetDataEvent
GetDataEvent.OnServerEvent:Connect(function(player, userID, stat, val)
local profile = Profiles[player]
GetDataEvent:FireClient(player, profile.Data[stat], val)
end)
return Profiles
Wrap the :GetAsync() and :SetAsyn() into pcalls. This is best practice, because if it fails without a pcall, the entire script with break for the whole server, and no player’s data will load or save. It also allows you to see the specific error of what goes wrong. The same as without it, just this time it won’t break the entire script.
local Data
local success, errorMessage = pcall(function()
Data = DataStore:GetAsync(Player.UserId)
end)
if success and Data ~= nil then
-- Actually do something with the loaded data
else
if errorMessage then
warn(errorMessage)
elseif Data == nil then
warn("User's data is nil")
end
end
You don’t even use this loaded data anywhere, so when it gets loaded, it just sits there.
Anyway, here is the saving as well
local success, errorMessage = pcall(function()
DataStore:SetAsync(Player.UserId, Player.leaderstats.Minutes.Value)
end)
if success then
print("Successfully saved player data")
else
warn(errorMessage)
end
You should also add a game:BindToClose() at the bottom of your script, so whenever the server is closing, it waits a certain amount of time so the data can fully save
game:BindToClose(function()
task.wait(60)
end)
Tell me whatever any error message is. If you don’t get any, please tell me.
Another thing I would recommend is putting all of the data loading and minutes coroutine into one PlayerAdded function.
A mostly ideal DataStore would look something like this:
game.Players.PlayerAdded:Connect(function(Player)
local Minutes = Player:WaitForChild("leaderstats"):WaitForChild("Minutes")
local Data
local success, errorMessage = pcall(function() --// A safety net for the script in case the call fails. This should be used with any API call (literally every built in function that has "Async" somewhere in it). If the API call fails, it will error and break the entire script. API calls can fail very easily, especially with player's that have slow internet or low-end devices. If it fails, it will only break inside of the pcall, and the rest of the script will run.
Data = DataStore:GetAsync(Player.UserId) -- Get Data
end)
if success and Data ~= nil then --// Checks if the pcall was successful and if the data is not equal to nil. If it can receive a successful call, but the user has no data, then it will still run just fine, but it will error when you try to apply the nil data to the value/property of something
print("Successfully loaded data for "..Player.Name)
else
warn(errorMessage)
end
if type(Data) ~= "table" then
Data = nil
end
--// Do something with the data (load it into the minutes value): Minutes.Value = Data
local incrementValue = 1 -- value when adding points
if (service:UserOwnsGamePassAsync(Player.UserId, GamepassId)) then -- 2x gamepass
incrementValue = 2
end
--[{ TIME GIVERS }]--
coroutine.resume(coroutine.create(function() -- gives 1 point every minute
while true do
wait(60) -- every minute
Minutes.Value = Minutes.Value + incrementValue -- adds points based off of the incrementValue
end
end))
end)
In the PlayerRemoving:
game.Players.PlayerRemoving:Connect(function(Player)
local success, errorMessage = pcall(function() --// A safety net for the script in case the call fails. This should be used with any API call (literally every built in function that has "Async" somewhere in it). If the API call fails, it will error and break the entire script. API calls can fail very easily, especially with player's that have slow internet or low-end devices. If it fails, it will only break inside of the pcall, and the rest of the script will run.
DataStore:SetAsync(Player.UserId, Player.leaderstats.Minutes.Value)
end)
if success then --// Checks if the pcall was successful
print("Successfully saved player data") --// Prints out if it was
else
warn(errorMessage) --// If it was not, it will print out the error message instead
end
end)
And the BindToClose so the server has time to save data before it shuts down (only when the last player in the server is leaving):
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("TimeStats")
local service = game:GetService("MarketplaceService")
local Profiles = {}
local GamepassId = 154681682
local saveStructure = {
Donated = 0;
Raised = 0;
Minutes = 0;
}
local PlayerProfileStore = ProfileService.GetProfileStore("PlayerSaveData", saveStructure)
local function PlayerDataLoaded(player)
local profile = Profiles[player]
local folder = Instance.new("Folder")
folder.Name = "leaderstats"
folder.Parent = player
local Donated = Instance.new("IntValue")
Donated.Name = "Donated"
Donated.Value = profile.Data.Donated
Donated.Parent = folder
local Raised = Instance.new("IntValue")
Raised.Name = "Raised"
Raised.Value = profile.Data.Raised
Raised.Parent = folder
local Minutes = Instance.new("IntValue")
Minutes.Name = "Minutes"
Minutes.Value = profile.Data.Minutes
Minutes.Parent = folder
spawn(function()
local profile = Profiles[player]
if profile ~= nil then
Donated.Value = profile.Data.Donated
Raised.Value = profile.Data.Raised
Minutes.Value = profile.Data.Minutes
end
end)
end
local function PlayerAdded(player)
local profile = PlayerProfileStore:LoadProfileAsync("Player_" .. player.UserId, "ForceLoad")
if profile ~= nil then
profile:ListenToRelease(function()
Profiles[player] = nil
player:Kick("Your profile has been loaded remotely. Please rejoin.")
end)
if player:IsDescendantOf(Players) then
Profiles[player] = profile
PlayerDataLoaded(player)
else
profile:Release()
end
else
player:Kick("Unable to load saved data. Please rejoin.")
end
end
for _, player in ipairs(Players:GetPlayers()) do
spawn(function()
PlayerAdded(player)
end)
end
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(function(player)
local profile = Profiles[player]
if profile ~= nil then
profile:Release()
end
end)
local function AddRaised(userID, amount)
local player = Players:GetPlayerByUserId(userID)
if player then
local profile = Profiles[player]
profile.Data.Raised = profile.Data.Raised + amount
end
end
local function AddDonated(userID, amount)
local player = Players:GetPlayerByUserId(userID)
if player then
local profile = Profiles[player]
profile.Data.Donated = profile.Data.Donated + amount
end
end
local function MinPlayed(userID, amount)
local player = Players:GetPlayerByUserId(userID)
if player then
local profile = Profiles[player]
profile.Data.Minutes = profile.Data.Minutes + amount
end
end
game.Players.PlayerAdded:Connect(function(Player)
local Minutes = Player:WaitForChild("leaderstats"):WaitForChild("Minutes")
local Data
local success, errorMessage = pcall(function() --// A safety net for the script in case the call fails. This should be used with any API call (literally every built in function that has "Async" somewhere in it). If the API call fails, it will error and break the entire script. API calls can fail very easily, especially with player's that have slow internet or low-end devices. If it fails, it will only break inside of the pcall, and the rest of the script will run.
Data = DataStore:GetAsync(Player.UserId) -- Get Data
end)
if success and Data ~= nil then --// Checks if the pcall was successful and if the data is not equal to nil. If it can receive a successful call, but the user has no data, then it will still run just fine, but it will error when you try to apply the nil data to the value/property of something
print("Successfully loaded data for "..Player.Name)
else
warn(errorMessage)
end
if type(Data) ~= "table" then
Data = nil
end
--// Do something with the data (load it into the minutes value): Minutes.Value = Data
local incrementValue = 1 -- value when adding points
if (service:UserOwnsGamePassAsync(Player.UserId, GamepassId)) then -- 2x gamepass
incrementValue = 2
end
--[{ TIME GIVERS }]--
coroutine.resume(coroutine.create(function() -- gives 1 point every minute
while true do
wait(60) -- every minute
Minutes.Value = Minutes.Value + incrementValue -- adds points based off of the incrementValue
end
end))
end)
game.Players.PlayerRemoving:Connect(function(Player)
local success, errorMessage = pcall(function() --// A safety net for the script in case the call fails. This should be used with any API call (literally every built in function that has "Async" somewhere in it). If the API call fails, it will error and break the entire script. API calls can fail very easily, especially with player's that have slow internet or low-end devices. If it fails, it will only break inside of the pcall, and the rest of the script will run.
DataStore:SetAsync(Player.UserId, Player.leaderstats.Minutes.Value)
end)
if success then --// Checks if the pcall was successful
print("Successfully saved player data") --// Prints out if it was
else
warn(errorMessage) --// If it was not, it will print out the error message instead
end
end)
game.ReplicatedStorage.Events.AddValue.Event:Connect(function(userID, amount, stat)
if stat == "Raised" then
AddRaised(userID, amount)
elseif stat == "Donated" then
AddDonated(userID, amount)
elseif stat == "Minutes" then
MinPlayed(userID, amount)
end
end)
local GetDataEvent = game.ReplicatedStorage.Events.GetDataEvent
GetDataEvent.OnServerEvent:Connect(function(player, userID, stat, val)
local profile = Profiles[player]
GetDataEvent:FireClient(player, profile.Data[stat], val)
end)
game:BindToClose(function()
task.wait(60)
end)
return Profiles
Click on the error and see which script it opens. If it wasn’t the data script, and you have a lot of scripts that run constantly, like in a realistic mesh pack for example, and it has moving leaves or something, that will happen a lot.
It happens to me a lot since I use a realistic mesh pack with a lot of leaf/plant movement scripts.
Also, when it goes white as if it is crashing, it isn’t. You’ll get used to it eventually, but that is what happens any time you have a BindToClose that waits a certain amount of time. It will go away after the designated amount of time, every single time you do it
Go up to the top of studio, click “View”, then “Command Bar”
Put this line of code into it and hit enter, and show me what it prints out, please:
With this, it does not matter if you test it in studio or the actual game.
I would like to introduce you to a plugin called “DataStore Editor.” It is a great plugin, and I use it literally daily, but the only downside is that it costs 300 Robux. There’s a chance you probably don’t just have that lying around to spend on a plugin. It was free for a short span of time at one point, and it costed 100 when I originally bought it a couple years ago.
If you do have the Robux and do want to look into buying it, then here is the link
If you do purchase it, using it is fairly self-explanatory, but if you need anymore guidance with it, I would be glad to help you with it.
Anyway, onto solving it without the plugin.
Since there isn’t any error messages being outputted I presume, it seems like it is saving just fine.
Back down in the PlayerRemoving function, where it prints out that the player data saved, add the line print("This is the data being saved: ", DataStore:GetAsync(Player.UserId)) just to see what it saves.
This is what that should look like:
game.Players.PlayerRemoving:Connect(function(Player)
local success, errorMessage = pcall(function() --// A safety net for the script in case the call fails. This should be used with any API call (literally every built in function that has "Async" somewhere in it). If the API call fails, it will error and break the entire script. API calls can fail very easily, especially with player's that have slow internet or low-end devices. If it fails, it will only break inside of the pcall, and the rest of the script will run.
DataStore:SetAsync(Player.UserId, Player.leaderstats.Minutes.Value)
end)
if success then --// Checks if the pcall was successful
print("Successfully saved player data") --// Prints out if it was
print("This is the data being saved: ", DataStore:GetAsync(Player.UserId))
else
warn(errorMessage) --// If it was not, it will print out the error message instead
end
end)
Print out the value itself before and after it saves, not what is in the DataStore. Keep that first line in there as well to see if what gets saved is the same as the before and after print messages:
game.Players.PlayerRemoving:Connect(function(Player)
local success, errorMessage = pcall(function() --// A safety net for the script in case the call fails. This should be used with any API call (literally every built in function that has "Async" somewhere in it). If the API call fails, it will error and break the entire script. API calls can fail very easily, especially with player's that have slow internet or low-end devices. If it fails, it will only break inside of the pcall, and the rest of the script will run.
print("Before:", Player.leaderstats.Minutes.Value)
DataStore:SetAsync(Player.UserId, Player.leaderstats.Minutes.Value)
print("After:", Player.leaderstats.Minutes.Value) --// Should be the same as the "before"
end)
if success then --// Checks if the pcall was successful
print("Successfully saved player data") --// Prints out if it was
print("This is the data being saved: ", DataStore:GetAsync(Player.UserId))
else
warn(errorMessage) --// If it was not, it will print out the error message instead
end
end)