Minute Counter Bug!

Wait real quick, I’ll check in Studio.

1 Like

DOing all of that inside Devforum is really annoying XD

2 Likes

XD. I deleted the unnecessary code, if it proves perfect on your end, I’ll proceed to test it on mine. Hope I can fix it! Thanks for all your help, y’all! EDIT; Tested it in my end and didn’t work, leaderboard for minutes disappeared once again.

1 Like

Add a Server Script called Leaderboard in ServerScriptService, and paste this:

local plrs = game:GetService("Players")
local added = plrs.PlayerAdded
local removing = plrs.PlayerRemoving

local DSS = game:GetService("DataStoreService")
local experiencestore = DSS:GetDataStore("MinutesPlayed")

added:Connect(function(plr)
	local leaderboard = Instance.new("Folder", plr)
	leaderboard.Name = "Leaderboard"

	local time = Instance.new("IntValue", leaderboard)
	time.Name = "Minutes Played"
	time.Value = 0

	local suc, result = pcall(function()
		return experiencestore:GetAsync(plr.UserId)
	end)

	if not suc then
		warn("Data Store Failed: ".. result)
		time.Value = 0
	else
		
		if result then
			time.Value = result
		end
	end
end)

-- Save the player's time value before leaving
removing:Connect(function(plr)
	local time = plr:WaitForChild("Leaderboard"):WaitForChild("Minutes Played")
	experiencestore:SetAsync(plr.UserId, time.Value)
end)

Add another Server Script in ServerScriptService named AddMinute and paste that:

local plrs = game:GetService("Players")
local added = plrs.PlayerAdded

added:Wait()

for i, v in pairs(plrs:GetChildren()) do
	local leaderboard = v:WaitForChild("Leaderboard")
	local timevalue = leaderboard:WaitForChild("Minutes Played")
	
	while task.wait(60) do
		timevalue.Value += 1
	end
end

Enable Studio Access to API Services inside Game Settings → Security → Enable Studio Access to API Services

1 Like

According to your code, the “Leaderboard” Script under ServerScriptService consists of X. According to your code the “AddMinute” Script under ServerScriptService consists of Y. The code is below.

X. local plrs = game:GetService(“Players”)
local added = plrs.PlayerAdded
local removing = plrs.PlayerRemoving

local DSS = game:GetService(“DataStoreService”)
local experiencestore = DSS:GetDataStore(“MinutesPlayed”)

added:Connect(function(plr)
local leaderboard = Instance.new(“Folder”, plr)
leaderboard.Name = “Leaderboard”

local time = Instance.new("IntValue", leaderboard)
time.Name = "Minutes Played"
time.Value = 0

local suc, result = pcall(function()
	return experiencestore:GetAsync(plr.UserId)
end)

if not suc then
	warn("Data Store Failed: ".. result)
	time.Value = 0
else

	if result then
		time.Value = result
	end
end

end)

– Save the player’s time value before leaving
removing:Connect(function(plr)
local time = plr:WaitForChild(“Leaderboard”):WaitForChild(“Minutes Played”)
experiencestore:SetAsync(plr.UserId, time.Value)
end)

Y. local plrs = game:GetService(“Players”)
local added = plrs.PlayerAdded

added:Wait()

for i, v in pairs(plrs:GetChildren()) do
local leaderboard = v:WaitForChild(“Leaderboard”)
local timevalue = leaderboard:WaitForChild(“Minutes Played”)

while task.wait(60) do
	timevalue.Value += 1
end

end
This doesn’t work on studio, I have API enabled. Did it work for you?

On mobile at the moment, I haven’t checked the first datastore reply in this thread but I’ll just assume that it works. Assuming that it works, then what’s missing is a game:BindToClose(function()
–code to save
end)
This is because Roblox studio shuts down the local server too fast for it to register and execute the save code when the server shuts down. Although you should set up a more proper way for the future, for quick testing of your code you should be able to save by just adding a task.wait(5) inside BindToClose.

Note that the server might shut down while on Roblox too, so it is crucial to add a game:BindToClose() function.

*I very much dislike typing on mobile, but hope you got something out of it :slight_smile: *

2 Likes

Sorry to bother you @21Witch, but do you know where I’d put the game:BindToClose(function()? I’d also like to know where to put the task.wait(5). Thanks!

You could place the BindToClose at the end of the script, add task.wait(5) inside the BindToClose function.

wait() is not deprecated but its marked as a legacy

1 Like

It worked for me. I recommend checking if it really didn’t work, because it is supposed to, as it did for me.

Could you reply to me with the final code? I’d appreciate it!

I already sent the final code to you. What is the problem you’re facing now?

You don’t need to count the seconds as the computer already does it for you. On Roblox you can use UNIX timestamp, which is the amount of seconds that have elapsed since 00:00:00 UTC on 1 January 1970. You can obtain a UNIX timestamp by using the built-in library os and calling its time method.

print(os.time()) -- 1714940821

-- 1 second later
print(os.time()) -- 1714940822

-- To obtain the elapsed time
print(1714940822 - 1714940821) -- 1

Now using this method you can obtain the amount of seconds player has been playing your game. When player joins your game you want to obtain the timestamp, and once the player leaves the game, you will want to obtain another timestamp and subtract the first timestamp from the newer one to obtain the amount of seconds that the player has been in your game.

local Players = game:GetService("Players")

local join_time: { [Player]: number } = {}

local function player_joined(player: Player)
	join_time[player] = os.time()
end

local function player_leaving(player: Player)
	if join_time[player] then
		local elapsed = os.time() - join_time[player]
		print(`{player.Name} has played the game for {elapsed} seconds`)
		join_time[player] = nil -- Cleanup, or there will be a memory leak!
	end
end

for _, player in Players:GetPlayers() do
	player_joined(player)
end

Players.PlayerAdded:Connect(player_joined)
Players.PlayerRemoving:Connect(player_leaving)

Now all you need to do is update the amount of seconds that the player has played your game for. There is many ways to save data, but for the sake of simplicity of this guide, I will show the simplest method. When player joins we will read their play time for debugging purposes, we might get nil which means that the key doesn’t exist, so player never had their play time recorded and we will treat it as 0. When player leaves, we will take their elapsed time, and add it to their total elapsed time which is saved in the data store, we will achieve this by using IncrementAsync method.

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")

local play_time_store = DataStoreService:GetDataStore("play_time")
local join_time: { [Player]: number } = {}

local function save_play_time(player: Player, elapsed: number)
	local success = pcall(function()
		play_time_store:IncrementAsync(tostring(player.UserId), elapsed, { player.UserId })
	end)

	if success then
		print(`Successfully saved {player.Name}'s play time`);
	else
		warn(`Failed to save {player.Name}'s play time`)
	end
end

local function read_play_time(player: Player): number?
	local success, data = pcall(function()
		return play_time_store:GetAsync(tostring(player.UserId))
	end)
	
	if success then
		-- If data is nil, that means the key doesn't exist
		-- if the key doesn't exist, the player's play time will be 0
		return if data == nil then 0 else data
	end
	
	return
end

local function player_joined(player: Player)
	join_time[player] = os.time()
	
	local play_time = read_play_time(player)
	if play_time then
		print(`{player.Name} has play time of {play_time} seconds`)
	else
		warn(`Failed to read {player.Name}'s play time`)
	end
end

local function player_leaving(player: Player)
	if join_time[player] then
		local elapsed = os.time() - join_time[player]
		print(`{player.Name} has played the game for {elapsed} seconds`)

		save_play_time(player, elapsed)
		join_time[player] = nil -- Cleanup, or there will be a memory leak!
	end
end

for _, player in Players:GetPlayers() do
	player_joined(player)
end

Players.PlayerAdded:Connect(player_joined)
Players.PlayerRemoving:Connect(player_leaving)

This is what you will see in your output window when you join for the first time, and leave:

Daw588 has play time of 0 seconds
Daw588 has played the game for 2 seconds
Successfully saved Daw588's play time

When you join again, and leave, this is what you will see:

Daw588 has play time of 2 seconds
Daw588 has played the game for 3 seconds
Successfully saved Daw588's play time

All we are doing essentially boils down to this:

local play_time = 0

local function simulate_session()
	local join_time = os.time()
	
	task.wait(math.random(1, 3))
	
	local leave_time = os.time()

	local elapsed = leave_time - join_time
	play_time += elapsed
end

-- First session

simulate_session()
print(play_time) -- 1

-- Second session

simulate_session()
print(play_time) -- 4

-- Third session

simulate_session()
print(play_time) -- 7

One last thing: If you want to display minutes to the player, you can convert seconds to minutes.

local play_time = 120 -- 120 seconds
-- Note: We are using math.floor to get rid of decimals because some numbers will produce non integer result
-- Note: There is 60 seconds in one minute
local minutes = math.floor(play_time / 60) -- 2 minutes
print(minutes) -- 2

This should get you started on the developing your play time saving feature. One thing to note is that the data saving solution is very simple and doesn’t account for situations when server crashes, or when player joins another server while their data is still saving in the previous one. Those situations can cause data loss! You will need to implement mechanisms like periodical data saves, and session locking. Though I wouldn’t recommend writing such system yourself, instead you should choose a battle tested data store solutions like DataStore2, and ProfileService.

1 Like

Thank you so much. That was very much informative, just to clarify I should put all of that code inside of one ServerScriptService Script?

Yes, it has to be on the server. Additionally, I forgot to include logic for removing join time when the player leaves, so make sure to do that unless you want to have a memory leak.

-- When player leaves remove their join time
join_time[player] = nil

Got it, I’m not on studio right now, but I’ll check when I get on. Thanks for all your help, y’all!

1 Like