How do you restart a function?


Goal

  • I want to create a function that is stoppable. An example would be, if a Attributes Changed function is triggered; it would stop a timer that once the timer is complete, would make the Attribute nil.

Issue

  • My issue is that I have no idea where to start. I have no idea if a coroutine would be a good way to go about this, plus I have little knowledge on coroutines.

What I’m trying to do

  • I’m currently attempting to create a tag system. Let me elaborate a bit more on what this tag system would do. When a player is tagged by other player, usually through an attack, the Attacker Attribute will be assigned the Attackers Name. After 10 seconds, unless the Attribute is assigned/Changed event has been triggered, it is to reset to a blank string. This also has to recognize if a player who is already in the Attacker Attribute has attacked the player and keep them in the Attribute for a little bit longer.

Script

game.Players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(function(Char)
		Char:SetAttribute("Attacker"," ")	
		Char:GetAttributeChangedSignal("Attacker"):Connect(function()
			if Char:GetAttribute("Attacker") ~= " " then
				local Num,Change = 0,false
				if Num ~= 0 then
					Num = 0
				end
				while Num < 10 do
					wait(1)
					Num += 1
					local CurrentNum = Num
					local Table2Print = {CurrentNum,Num}
					warn(Table2Print)
					if Num < CurrentNum then
						break
					end
				end
				if Num == 10 then
					Char:SetAttribute("Attacker"," ")
					Num = 0
				else
					Num = 0
				end
			end
		end)
	end)
end)


Any assistance is appreciated.

Have you looked into using the ‘:GetAttributeChangedSignal()’ method to detect when the values of an object’s attributes are changed?

local Game = game
Game:SetAttribute("Attribute", math.pi)

local function Function()
	print(Game:GetAttribute("Attribute"))
end

Game:GetAttributeChangedSignal("Attribute"):Connect(Function)

for _ = 1, 10 do
	Game:SetAttribute("Attribute", math.random())
end
1 Like

Well, the issue is; I’m technically already doing that in my one version; the issue is, there’s one for every single player and gets triggered at different times for each player. With all the trouble I was having, I figured simply just swapping to a new method would be wiser.

Old Coding, updated the original post to reflect this.
game.Players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(function(Char)
		Char:SetAttribute("Attacker"," ")
		SignalListeners.Listeners[Char] = Char:GetAttributeChangedSignal("Attacker"):Connect(function()
			if SignalListeners.Timers[Char] then
				SignalListeners.Timers[Char] = nil
			end
			SignalListeners.Timers[Char] = function(Char)
				wait(10)
				Char:SetAttribute("Attacker","")
			end
			SignalListeners.Timers[Char](Char)
		end)
	end)
end)

This is actually possible now that task.cancel is a thing:

--your custom function
function yourFunction(name) 
	while task.wait() do 
		print("Hi", name)
	end
end

--creates and runs the thread
function startAsync(f, ...)
	local thread = task.spawn(f, ...) 
	return thread 
end

--kills the thread
function stopAsync(thread) 
	task.cancel(thread) 
end 

--uses the two processes mentioned above to restart the thread
local thread 
function restart(f, ...)  
	stopAsync(thread)
	--remove the 3 lines below, I added them to vizualize the process
	print("function stopped!") 
	task.wait(2) 
	print("function restarted!") 
	thread = startAsync(f, ...)
end

thread = startAsync(yourFunction, "david") 
task.wait(5) --wait 5 seconds
--restart must be called AFTER defining the thread above
restart(yourFunction, "david") --restarts the function

Edit: I wrote a small object to make this process much easier(Module link), it gives the same results with just 4 lines of code:

function func(name) 
	while task.wait() do 
		print("Hi", name)
	end
end

local ASYNC = require(script.AsyncFunc)
local async = ASYNC.new(func, "david") 
async:start() 
task.wait(2) 
--if parameters are passed inside restart, they override the old function parameters
async:restart("bob")
1 Like

You are better off using os.clock and a RS.Heartbeat bind.

local Tags : {[Player] : RobloxScriptSignal} = {}

game.repstorage.remote.onserverevent:Connect(function(Player : Player) 
if not Tags[Player] then Tags[Player] = os.clock() + 10 end -- seconds end
if Tags[Player] then Tags[Player] = os.clock + 5 end -- add lesser time
end)


RunService.Heartbeat:Connect(function()
for Index : Player, Value : Time in next, Tags do
if Value < os.clock() then
Tags[Index] = nil
end
end)
1 Like