My corrountine doesn't work as intended

I want to make a function where if the player uses the key X it will fire a remote event in a loop and if the player uses X again it will stop.

The problem is that even with the corrountine the event will not fire as if the thread was occuped

that’s my code:

local UIS = game:GetService("UserInputService")
local db = os.clock()
local Inputing = false

local function Meditate(Input,gp)
	if gp or (os.clock() - db) < 1 then return end 
	if Input.KeyCode == Enum.KeyCode.X then
		if Inputing == false then 
			Inputing = true
			print(" X Pressed")
			coroutine.resume(FireMeditation)
			db = os.clock()
		elseif Inputing == true then
			coroutine.yield(FireMeditation)
			print("hm")
			Inputing = false
			MeditateEvent:FireServer("Stop")
			db = os.clock()
		end	
	end
end

FireMeditation = coroutine.create(function()
    while true do
		task.wait(1)
		MeditateEvent:FireServer("Meditate")
		print("Test")
	end
	
end)

UIS.InputBegan:Connect(Meditate)

the first time I tested the function I used corrountine.close to stop and it worked.

1 Like

unfortunately you can’t yield a coroutine outside of a coroutine. Why are you firing the server every one second to begin with tho?

Regardless you want to create the coroutine when you want to actually use it

local UIS = game:GetService("UserInputService")
local db = os.clock()
local Inputing = false
local MeditationThread
--^^Define the local variable to show it will be in this context

local function Meditate(Input,gp)
	if gp or (os.clock() - db) < 1 then return end 
	if Input.KeyCode == Enum.KeyCode.X then
		if Inputing == false then 
			Inputing = true
			print(" X Pressed")
            --Define the coroutine when you actually want to create it
            --Using task.spawn because you need to coroutine.resume threads
			MeditationThread = task.spawn(function()
               while true do
		            task.wait(1)
		            MeditateEvent:FireServer("Meditate")
		            print("Test")
	           end
            end)

			db = os.clock()
		elseif Inputing == true then
            --Check if meditation thread and if so then close it
			if MeditationThread then
                 coroutine.close(MeditationThread)
                 MeditationThread = nil
            end
			print("hm")
			Inputing = false
			MeditateEvent:FireServer("Stop")
			db = os.clock()
		end	
	end
end

UIS.InputBegan:Connect(Meditate)

I wouldn’t have the client firing the server every second tho, as that’s a huge security risk (exploiters can fire the event when they arent supposed to and also fire it multiple times per second)

Instead I would suggest firing a remote event when the player toggles their meditation and then having the server handle it

If your script is in ServerScriptService then you will need a cache of player inputs however which is complicated. But if you put the localscript and serverscript in StarterPlayerScripts then you can have it per player. (but thats cringe so ill show you how to cache)

Client

MeditationRemote:FireServer("Meditate")
Server

--create our meditation cache to begin with
local MeditationCache = {}

MeditationRemote.OnServerEvent:Connect(function(plr)
   --grab the meditation cache for this player
   local PlayerMeditationCache = MeditationCache[plr]

   --simple statement saying if the player's cache doesnt exist then return and do nothing
   if not PlayerMeditationCache then return end
   
   --clear the old coroutine
   --we will always want just one coroutine
   --so this ensures the slate is wiped clean whether we are turning it off or on
   if PlayerMeditationCache.MeditationThread then
      coroutine.close(PlayerMeditationCache.MeditationThread)
      PlayerMeditationCache.MeditationThread = nil
   end

   if PlayerMeditationCache.IsMeditating then
      --IsMeditating is true, toggle it off
      PlayerMeditationCache.IsMeditating = false
   else
      --IsMeditating is false, toggle it on
      PlayerMeditationCache.IsMeditating = true
      PlayerMeditationCache.MeditationThread = task.spawn(function()
         repeat
            task.wait(1)
            --do stuff here
         until false
      end)
   end
end)

--player added to add in the player's data
game.Players.PlayerAdded:Connect(function(plr)
   --set the player's meditation cache to blank data
   MeditationCache[plr] = {
      IsMeditating = false;
      MeditationThread = nil;
   }
end)

--player removing to clean up memory leaks
--we want to clean the player cache when they leave
--or else the player object will remain in memory forever and thats cringe
game.Players.PlayerRemoving:Connect(function(plr)
   MeditationCache[plr] = nil
end)

hopefully this helps and have a good day/night!

1 Like

Thank you so much, It worked and look more clean that my last code, also I didn’t know about cache, good night/day.

But there’s something I didn’t understand, in “MeditationCache[plr]” what is the value between the “[ ]” ? a index? I couldn’t understand it even in the roblox documentation.

the value between the [] is an index. With dictionaries you have indexes and values. However indexes do not always need to be strings. In this case the indexes are the player objects while the value is the data you want to cache

local Dict = {
  [game.Workspace] = "goofy ahh"
  --game.workspace for key, goofy ahh for value
}

print(Dict[game.Workspace])--prints "goofy ahh"
1 Like

Okay, thanks bro, I thought that the index in a dictionary was the keys, so the only way to make a different dictionary for every player was with arrays or something like that.

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