RunService.HeartBeat function

I have this part of my script which for every second adds one to the seconds’ value. Every second it goes through all the players and adds 1. It works fine with one player but with two or more players, one of them might be going up by two(the others’ might not even go up) and it starts randomly going up by one, two etc.

The part:


			RunService.Heartbeat:Connect(function()
	if not (tick() >= NextStep) then return false end
	NextStep = NextStep + 1
	local Plrs = Players:GetChildren()
	for index = 1, #Plrs do
		Player.leaderstats.Seconds.Value += 1
	end
end)

The whole script:

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HttpService = game:GetService("HttpService")
local RunService = game:GetService("RunService")
local checkpoints = workspace.Checkpoints
local ga = false
local NextStep = tick()
Players.PlayerAdded:Connect(function(Player)
	local PlayerDataStore = DataStoreService:GetDataStore("PlayerStore", Player.UserId)
	warn("Initializing leaderstats...")
	local stats = Instance.new("Folder")
	stats.Name = "leaderstats"
	stats.Parent = Player
	local wipeouts = Instance.new("IntValue")
	wipeouts.Name = "Deaths"
	wipeouts.Parent = stats
	local second = Instance.new("IntValue")
	second.Name = "Seconds"
	second.Parent = stats
	 local stage = Instance.new("IntValue")
    stage.Name = "Stage"
	stage.Parent = stats

	print("Completed.")
	
	warn("Initializing leaderstats values...")
	local success = PlayerDataStore:GetAsync("success")
	local PlayerData = PlayerDataStore:GetAsync("PlayerData")
	if success == nil or false then
		print("Player is currently nil")
		PlayerDataStore:SetAsync("success", true)
		PlayerDataStore:SetAsync("PlayerData", HttpService:JSONEncode({deaths = 0, sec = 0, stages = 0}))
		Player:Kick("Rejoin, you have no data")
	elseif success == true then
		local decoded = HttpService:JSONDecode(PlayerData)
		--for k,v in pairs(decoded) do
		--print(k, v, type(v))
		--end
		Player.leaderstats.Deaths.Value = decoded.deaths
		Player.leaderstats.Seconds.Value = decoded.sec
		Player.leaderstats.Stage.Value = decoded.stages
		print(success)
		print("Completed.")
		warn("Continuing with normal RunTime")
		Player.CharacterAdded:connect(function(Character)
			local humpart = Character.HumanoidRootPart
			RunService.Stepped:wait()
        humpart.CFrame = checkpoints[stage.Value].CFrame+ Vector3.new(0,math.rad(3.5),0)
			local d = true
			Character:WaitForChild("Humanoid").Died:Connect(function()
			RunService.Stepped:wait()
        humpart.CFrame = checkpoints[stage.Value].CFrame + Vector3.new(0,math.rad(3.5),0)
				if d then
					Player.leaderstats.Deaths.Value += 1
				end
			end)

			RunService.Heartbeat:Connect(function()
	if not (tick() >= NextStep) then return false end
	NextStep = NextStep + 1
	local Plrs = Players:GetChildren()
	for index = 1, #Plrs do
		Player.leaderstats.Seconds.Value += 1
	end
end)
		
		end)
	end
end)

Players.PlayerRemoving:Connect(function(Player)
	ga = true
	local PlayerDataStore = DataStoreService:GetDataStore("PlayerStore", Player.UserId)
	warn(string.format("%s IN QUEUE...", Player.Name:upper()))
	local death = Player.leaderstats.Deaths.Value
	local secs = Player.leaderstats.Seconds.Value
	local stagess = Player.leaderstats.Stage.Value
	local data = {deaths = death,sec = secs, stages =stagess }
	local encoded = HttpService:JSONEncode(data)
	PlayerDataStore:SetAsync("PlayerData", encoded)
	print("Done")
end)
1 Like

Try putting the heartbeat function inside a LocalScript. I think it’s the GetChildren function that’s delaying it.

That would lag a lot I assume, you could use FireServer() from the client to add the seconds.

You should also use GetPlayers().

You can not access the leaderstats of players from a local script.

Yes, you can. But any changes you do locally is not replicated.

How would I do that (30charsss)

You can with remote events (30chars)

local Player = game:GetService('Players').LocalPlayer
local Stats = Player:WaitForChild('leaderstats')

I mean what Xueify said (30 cha)

How would I do the fire server thing (a code sample will be helpful)

tick() will always be greater than the number value you have, so it doesn’t add one per second, it adds one per frame which is up to 60 per second.

Maybe you meant to use os.time() or math.floor( tick() ) ?

How can I do that (30charssss)

I just put how to do that. Replace tick() with one of the two options. Also are you using luau? Otherwise the += will fail.

local NextStep = os.time()

-- later on, NOT inside the PlayerAdded connection:
RunService.Heartbeat:Connect(function()
	if not (os.time() > NextStep) then return false end
    local diff = os.time() - NextStep
	NextStep = os.time()
	local Plrs = Players:GetChildren()
	for index = 1, #Plrs do
        if Plrs[index]:FindFirstChild('leaderstats') then
		    Plrs[index].leaderstats.Seconds.Value += diff
        end
	end
end)

Don’t do it from the client, you’ll get all of the second going up at slightly different times, plus people can exploit and send you a tonne of events to boost their stats.

There is not going to be any lag from 1 heartbeat connection on the server that does such a minimal task.

Do not put the connection inside of PlayerAdded.

LocalScript:

remote = -- remote event goes here

t = 0
RunService.Heartbeat:Connect(function(step)
    t += step
    if not (t > NextStep) then return false end
    NextStep = NextStep + 1
    remote:FireServer()
    t = 0
end)

Server Script:

remote.OnServerEvent:Connect(function(player)
   -- change player values here
end)
local Event = game:GetService('ReplicatedStorage').Event
Event.OnServerEvent:Connect(function(Player, ...)
	-- You should probably check how many seconds has passed since the last :FireServer().
	-- It would be needed as exploiters can do FireServer() with any arguments.
end)

-- Client
local Event = game:GetService('ReplicatedStorage'):WaitForChild('Event')
-- Your code
Event:FireServer()

And, for the waiting part:

local StartTime = tick()
wait(1)
local EndTime = tick()
print(EndTime - StartTime)

Where do I put this in my script or do I make a new local script and if so how do I call the players seconds leaderstats.

I’ve edited my earlier reply to contain some more info and code. Don’t do it from a localscript.

1 Like

yes thank you so much, it works now.

https://prnt.sc/t6u5u1

Based off of that, looping through every player and checking that often might cause some lag issues.
Performance-wise, I believe the client should handle the HeartBeat event, and let the server know afterwards.

1 Like

Looping through every player in a heartbeat event to simply add a value will not lag.

At the very least test it before making claims and overcomplicating things for a beginner scripter and putting ideas into their head that extremely short and quick Heartbeat connections are bad. They aren’t if you use them properly. 59 of the 60 frames it won’t run anything beyond the first inequality anyway.

One of the benefits of using Heartbeat is that it doesn’t delay physics (or rendering if on the client) so even if it does run over once per second it’s not the end of the world. But it won’t.

2 Likes