Trouble with datastoring a minutes and hours leaderstats system

Hey everyone! Before you continue reading this post, I would like to point out one thing. I have little to no experience with scripting in studio. I’m a builder. :melting_face:
This is the script I have so far. I mostly compiled it from forum posts, and toolbox leaderstat scripts. This probably looks whack to advanced scripters lol.

local players = game:GetService("Players")
local datastores = game:GetService("DataStoreService")
local datastoreMinute = datastores:GetDataStore("DataStoreTime")

local function onPlayerAdded(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	

	local hours = Instance.new("IntValue")
	hours.Name = "Hours"
	hours.Parent = leaderstats

	local minutes = Instance.new("IntValue")
	minutes.Name = "Minutes"
	minutes.Parent = leaderstats
	
	

	
	
	
	
	
	
	
	local success, result = pcall(function()
		return datastoreMinute:GetAsync("Minutes_"..player.UserId)
	end)



	
	
	task.spawn(function()
		while task.wait(.1) do
			minutes.Value += 1
			if minutes.Value >= 60 then 
				minutes.Value = 0 
				hours.Value += 1 
			end
		end
	end)
end




local function onPlayerRemoving(player)
	local minutes = player.leaderstats.Minutes
	local hours = player.leaderstats.Hours
	local success, result = pcall(function()
		datastoreMinute:UpdateAsync("Minutes_"..player.UserId, function(old)
			return minutes.Value 
		end)
	end)
end


players.PlayerAdded:Connect(onPlayerAdded)
players.PlayerRemoving:Connect(onPlayerRemoving)

Does anyone know why the datastore isn’t saving? Thanks. :sweat_smile:

2 Likes

Well one, you never even set the value of minutes after getting the data. Also, you never even save Hours, but I assume that is on purpose.

local players = game:GetService("Players")
local datastores = game:GetService("DataStoreService")
local datastoreMinute = datastores:GetDataStore("DataStoreTime")

local function onPlayerAdded(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local hours = Instance.new("IntValue")
	hours.Name = "Hours"
	hours.Parent = leaderstats

	local minutes = Instance.new("IntValue")
	minutes.Name = "Minutes"
	minutes.Parent = leaderstats
	
	local success, result = pcall(function()
		return datastoreMinute:GetAsync("Minutes_"..player.UserId)
	end)

	if success then
		minutes.Value = result
	end

	task.spawn(function()
		while task.wait(.1) do
			minutes.Value += 1
			if minutes.Value >= 60 then 
				minutes.Value = 0 
				hours.Value += 1 
			end
		end
	end)
end

local function onPlayerRemoving(player)
	local minutes = player.leaderstats.Minutes
	local hours = player.leaderstats.Hours
	local success, result = pcall(function()
		datastoreMinute:UpdateAsync("Minutes_"..player.UserId, function(old)
			return minutes.Value 
		end)
	end)
end

players.PlayerAdded:Connect(onPlayerAdded)
players.PlayerRemoving:Connect(onPlayerRemoving)

That was not on purpose. I have like zero experience with scripting. I only build with studio. :smiling_face_with_tear:

Ah, alright. This should work for what you are trying to do:

local players = game:GetService("Players")
local datastores = game:GetService("DataStoreService")
local datastore = datastores:GetDataStore("DataStoreTime")

local function onPlayerAdded(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local hours = Instance.new("IntValue")
	hours.Name = "Hours"
	hours.Parent = leaderstats

	local minutes = Instance.new("IntValue")
	minutes.Name = "Minutes"
	minutes.Parent = leaderstats
	
	local success, result = pcall(function()
		return datastore:GetAsync("Time_"..player.UserId)
	end)

	if success then
		minutes.Value = result.minutes
		hours.Value = result.hours
	end

	task.spawn(function()
		while task.wait(.1) do
			minutes.Value += 1
			if minutes.Value >= 60 then 
				minutes.Value = 0 
				hours.Value += 1 
			end
		end
	end)
end

local function onPlayerRemoving(player)
	local minutes = player.leaderstats.Minutes
	local hours = player.leaderstats.Hours
	local success, result = pcall(function()
		datastore:UpdateAsync("Time_"..player.UserId, function(old)
			return {minutes = minutes.Value, hours = hours.Value}
		end)
	end)
end

players.PlayerAdded:Connect(onPlayerAdded)
players.PlayerRemoving:Connect(onPlayerRemoving)
1 Like

Please don’t increment the hours and minutes after waiting for a certain time. It’s very inaccurate.
Here’s a rewrite:

local playersService = game:GetService("Players");
local datastoreService = game:GetService("DataStoreService");

local timeStore = datastoreService:GetDataStore("time_1");

local function sToHM(s)
	local hours = math.floor(s / 3600);
	local minutes = math.floor(s / 60) % 60;
	return hours, minutes;
end

local function getDelta(t1, t2)
	return math.round(t2 - t1);
end

local function makeLeaderstat(player: Player)
	local success, data = pcall(timeStore.GetAsync, timeStore, player.UserId);
	if not success then
		warn("There was an error while loading", tostring(player.UserId) .. "'s", "data");
		return;
	end
	data = tonumber(data) or 0;
	
	local h,m = sToHM(data);
	local now = os.time();	
	
	local leaderstats = Instance.new("Folder");
	leaderstats.Name = "leaderstats";
	
	local hours = Instance.new("IntValue");
	hours.Value = h or 0;
	hours.Name = "Hours";
	
	local minutes = Instance.new("IntValue");
	minutes.Value = m or 0;
	minutes.Name = "Minutes";
	
	local joinTime = Instance.new("IntValue");
	joinTime.Name = "JoinTimestamp";
	joinTime.Value = now;
	
	task.defer(function()
		repeat
			task.wait(0.25);
			hours.Value, minutes.Value = sToHM(getDelta(os.time(), now) + data);
		until not player or not player:IsAncestorOf(game);
	end);
	
	hours.Parent = leaderstats;
	minutes.Parent = leaderstats;
	joinTime.Parent = player;
	leaderstats.Parent = player;
end

local function save(player)
	local secondsSpent = getDelta(os.time(), player.JoinTimestamp.Value);
	timeStore:IncrementAsync(player.UserId, secondsSpent);
end

playersService.PlayerAdded:Connect(makeLeaderstat);
for _,p in ipairs(playersService:GetPlayers()) do
	makeLeaderstat(p);
end

playersService.PlayerRemoving:Connect(save);
game:BindToClose(function()
	for _,p in ipairs(playersService:GetPlayers()) do
		save(p);
	end
end);
1 Like

You do realize that you honestly are going against yourself? One, the code you made is much, much longer. Two, task.wait() does not throttle like wait(). That post you referenced is from THREE years ago and is actually quite outdated. In fact, the task wait quite literally does the exact same thing as yours, with much less code. And, as for preventing errors, they could simply add a check to determine whether the player still exists or not.

1 Like

I’m not going against myself. I’m not against using wait() or task.wait() as a whole, I use them too, but however, using them with time use cases is a very bad practice. I also agree with you that task.wait() is much better than wait(). Timestamp math always yields an accurate result while wait() doesn’t. Even I’m using task.wait() in the code but calculating the time isn’t relying on it at all. I’m simply using it to visually update the time elapsed on the leaderboard.

Also, longer code doesn’t mean inefficient/worse. Better practice is always preferred.

1 Like

Hey! So this script worked for the most part, but it did cause a problem.


It works perfect when 2 people are in the same server, but when a third person joins, I get this error. Their leaderstats don’t update or save either. Thank you

That’s quite weird, it shouldn’t even run that part of the code if they have no data, but this should be a fix:

local players = game:GetService("Players")
local datastores = game:GetService("DataStoreService")
local datastore = datastores:GetDataStore("DataStoreTime")

local function onPlayerAdded(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local hours = Instance.new("IntValue")
	hours.Name = "Hours"
	hours.Parent = leaderstats

	local minutes = Instance.new("IntValue")
	minutes.Name = "Minutes"
	minutes.Parent = leaderstats
	
	local success, result = pcall(function()
		return datastore:GetAsync("Time_"..player.UserId)
	end)

	if success and result then
		minutes.Value = result.minutes
		hours.Value = result.hours
	end

	task.spawn(function()
		while task.wait(.1) do
			minutes.Value += 1
			if minutes.Value >= 60 then 
				minutes.Value = 0 
				hours.Value += 1 
			end
		end
	end)
end

local function onPlayerRemoving(player)
	local minutes = player.leaderstats.Minutes
	local hours = player.leaderstats.Hours
	local success, result = pcall(function()
		datastore:UpdateAsync("Time_"..player.UserId, function(old)
			return {minutes = minutes.Value, hours = hours.Value}
		end)
	end)

	if not success then
		warn("Problem saving "..player.Name.."'s data")
	end
end

players.PlayerAdded:Connect(onPlayerAdded)
players.PlayerRemoving:Connect(onPlayerRemoving)

game:BindToClose(function()
	for i,v in pairs(players:GetPlayers()) do
		onPlayerRemoving(v)
	end
end)

here is what I did, I made sure it was success and that there was a result on the getting data, and I added a BindToClose for when the server shuts down and PlayerRemoving doesn’t always run.

From what I can tell, this solved the problem. Once again, thank you!!