Help with stats

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

Why wont my minutes save? How do I fix this issue

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:

Hello, thank you for the response! Could you paste the code into what I have so I could better understand?

Sure! Here is the code with the pcall additions.

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):

game:BindToClose(function()
	task.wait(60)
end)

Wow I appreciate that so much! One bug I found is whenever I end studio test play it crashes and hits me with this error

Not running script because past shutdown deadline (x86)


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

Yeah, when I click the error no script comes up. Unfortunately the script still doesn’t save my minutes

Perhaps do you have a discord that way communication is easier?

What I want you to do now is this:

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:

print(game:GetService("DataStoreService"):GetDataStore("TimeStats"):GetAsync(player.UserId))

Also, I do not like share my discord with anyone unless it’s for commissions. I deeply apologize.

Completely understand no worries! Here is the results I get when pasting

I’m so stupid. I’m sorry. I put player.UserId as if it was in the script :rofl:

Replace player.UserId with your UserId. It should be 1473581729 (got it from your roblox profile lol)

Lol no worries :joy:. Here is the results after changing

It didn’t return nil, so it is saving.

Now please try it again with my UserId, which is 56437368
It should return nil this time since I don’t have any data in the place.


One thing that I’m fairly sure you have already done, make sure “Studio Access to API Services” is enabled.

Also, since it is saving, you probably didn’t change one of my commented lines in my code a bit ago. This one specifically:

You should replace that line with just Minutes.Value = Data so it actually loads the data into the value, otherwise it still isn’t doing anything.

Indeed it does print nil. I will change that comment to an actual line of code and test it once again

Hmmmm it still doesnt save. Should I be testing in the actual game or in studio? I also do have the Studio Access to API Services enabled.

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)


Here is what the output says, however when I load back in it still doesnt save

Try something similar.

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)


Same thing, it wont save in studio