Updating of a leaderstat Value Delaying

local ServerScriptService = game:GetService("ServerScriptService")
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local OnButtonClick = ReplicatedStorage.RemoteEvents.OnButtonClick
local SecretValueIncrease = ServerStorage.BindableEvents.SecretValueIncrease

local DataManager = require(ServerScriptService.Data.DataManager)

local debounce = false

OnButtonClick.OnServerEvent:Connect(function(player)
	local profile = DataManager.Profiles[player]

	if not debounce then
		debounce = true
		task.wait(.5)

		profile.Data.Clicks += 1
		player:WaitForChild("leaderstats"):WaitForChild("Clicks").Value = profile.Data.Clicks

		debounce = false
	end
end)

I’m trying to make it so that every time I click a button, one gets added to the “Clicks” IntValue, but there is a delay while adding it (no, not the task.wait(.5) one.)

I tried to make a function for adding the value in the DataManager module, but it didn’t work either, and it had the same delay.

1 Like

Im guessing it is because it’s a remote event? Remote events will always be a bit delayed.

You declared task.wait(0.5) before the changes into the stat changes, so this is what your code was doing:

  • Get profile
  • Check for debounce, if debounce is false then continue
  • Set debounce to true
  • waited for 0.5 seconds
  • Set the values onto the player’s profile and leaderstats.

Side note, unless you’re working on a single-player project, debounce will remain false for every player in the server for 0.5 seconds, since all RemoteEvent calls access the same debounce variable.

You can counteract this by setting debounce as a table instead. I’d also try to avoid using task.wait for debounces. You can use time() instead to track when the player last clicked, then whenever the server receives the RemoteEvent, do an if statement to check if it’s been 0.5 seconds past their last time clicked.

local cooldown = 0.5
local debounces = {}

OnButtonClick.OnServerEvent:Connect(function(player)
	-- time() returns the exact second when called since server startup
	if debounces[player] == nil or debounces[player] + cooldown < time() then
		-- set the player's debounce to the time() value right now
		debounces[player] = time()

		-- access the profile and change the value
		local profile = DataManager.Profiles[player]
		local leaderstats = player:WaitForChild("leaderstats")
		local clickCount = leaderstats:WaitForChild("Clicks")
		profile.Data.Clicks += 1
		clicks.Value = profile.Data.Clicks
	end
end)

-- New: Remove the player from the table when they leave the server
game.Players.PlayerRemoving:Connect(function(playerLeaving)
	-- check if player is inside the debounce table
	if debounces[player] then
		-- remove the player from the debounces table
		debounces[player] = nil
	end
end)

That’s all you need for your code to work. Let me know if this doesn’t achieve your desired outcome.

Extra: Encapsulation!

If you want to make your code a lot more easier and readable, continue on reading!

I’m also using ProfileService for one of my projects. Since you’re using ProfileService (I’m assuming so since you’re calling Profiles from DataManager), you can make your code a lot more readable and reusable by putting this in your DataManager Module:

function DataManager.Increment(player, statName, delta)
	-- get the profile of the player
	local profile = DataManager.Profiles[player]
	-- get the stat you want to change
	if profile.Data[statName] == nil then
		warn("DataManager.Increment: statName <" ..statName.. "> does not exist! Have you spelt the value name incorrectly?")
		return nil
	end

	profile.Data.Clicks += delta
	local newValue = profile.Data.Clicks

	-- change the leaderstats value if it exists
	local leaderstats = player:FindFirstChild("leaderstats")
	if leaderstats == nil then
		return newValue
	end
	local stat = leaderstats:FindFirstChild(statName)
	if stat then
		-- since the value we want to change has an associated leaderstat, replicate the changed value onto the leaderstat.
		stat.Value = newValue
	end
	return newValue
end 

With the same guidelines, you can also create more functions such as DataManager.GetValue(), or DataManager.SetValue(), functions that manipulate or grab your values without actually needing to ever worry about grabbing them improperly.

Now your RemoteEvent function can look like this:

local cooldown = 0.5
local debounces = {}

OnButtonClick.OnServerEvent:Connect(function(player)
	-- time() returns the exact second when called since server startup
	if debounces[player] == nil or debounces[player] + cooldown < time() then
		-- set the player's debounce to the time() value right now
		debounces[player] = time()

		-- access the profile and change the value
		DataManager.Increment(player, "Clicks", 1)
	end
end)

-- New: Remove the player from the table when they leave the server
game.Players.PlayerRemoving:Connect(function(playerLeaving)
	-- check if player is inside the debounce table
	if debounces[player] then
		-- remove the player from the debounces table
		debounces[player] = nil
	end
end)

Now your code looks a lot more neat, smaller, and readable!

This is called encapsulation, where you have one script, DataManager, store the actual logical code for value changes, and whenever you want other scripts to change the values, you can ask DataManager to make changes by using its functions DataManager.Increment(), etc.

1 Like

This works exactly as I want it to, thanks! I used the encapsulation method. A really nice way to use the time function. Two questions though, should I use time instead of debounce even in local scripts? The other question is why not use “not” instead of “nil”?

For example, if not debounces[player]… instead of if debounces[player] == nil… Is that an accuracy thing?

For local/client scripts, you can still use either/or and it will achieve the same outcome, so I recommend the method you are most comfortable with.

If you are going to use time() to track debounces, rather than having debounce = {} you could just set debounce = time() since only the player has access to debounce.

Me using nil in this scenario was just a personal preference. not will check if a value is false or nil. Using either not or == nil will achieve the same outcome.

I tend to use nil because I’m working on a project that needs to check if a value is true, false, or nil, so three different outcomes, I’ve been doing it so long it’s second nature lol

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.