TheMostAnnoyingBug

  1. I want a function to return as soon as a table’s size is below one element, in other words, the function should return as soon as the table has nothing in it.

  2. The issue is that the function has a wait(1) in it, and during that period, if the table’s size changes from 1 to 0 and then 1 super fast, the function will not return because it doesn’t check the table’s size until after the wait(1) period (it doesn’t check the table’s size during the wait(1) period.

  3. I tried to think of many methods to get around this, but probably due to my limited scripting knowledge, I haven’t found a way to do it.

function module.countDown(n, t)
	if module.checkNumber(t) then
		if n == 0 then
			stringValue.Value = "Success" 
		else
			stringValue.Value = "Intermission in " .. n - 1
			wait(1)
			module.countDown(n - 1, t)
		end
	else
		boolValue.Value = false
		stringValue.Value = "Not enough players"
		return
	end
end
      

Here my studio if you want to get a better understanding what I’m trying to do here. Just click play button and run the game. And then click on the gui super fast and the problem is that the countdown number doesn’t reset when the player goes out and then in again because your changing the table’s size from 1 to 0 and then to 1 again in the wait(1) period.ForOzzy.rbxl (47.7 KB)

2 Likes

Once you call wait(n), there’s no stopping it. Roblox’s Task Scheduler will always suspend the thread until n seconds have passed, or very close to it. You’ll have to use a tighter loop that checks more frequently than 1 second, if you want more resolution than 1 second. This is a easy, but messy fix and overall something you want to avoid (if for anything, performance reasons).

Looks like what you’re trying to do is “Be in an intermission until X players are present”. Your recursive solution seems unnecessary, as an iterative (a for, while, or do-while loop) may be easier:

function module.countDown(seconds, players) -- renamed n and t to be meaningful here
    while module.checkNumber(players) do
        stringValue.Value = "Intermission in " .. seconds
        seconds = seconds - 1
        wait(1)
    end
    if module.checkNumber(players) then
        stringValue.Value = "Success"
        return true
    else
        stringValue.Value = "Not enough players"
        return false 
    end
end

You’ll notice I added return true and return false – this is because this function should probably indicate to whatever called it whether another countdown is necessary. In my example above, false indicates another countdown is necessary, but really you could use whatever you want here.

Hope that helps!

1 Like

Hi, this is a common problem, the best solution is probably to check the number after you changed it, for example in a dedicated changeNumber() function.

Also, I notice that you use recursion instead of a loop. I would recommend a loop, even though in this case it works fine because the countdown time is probably not a very high number. It’s better to work in patterns that scale well.

It also seems to me, that your return statement does nothing. Returning is what a function does when it reaches the end, the keyword is not neccessary for it.

It feels like you are overthinking it, with the clever recursive solution (it is clever). Try to let the events be the trigger (for example to change boolValue.Value), and in your loop just check whether the game ended. I’m pretty sure you will work it out soon, good luck!

I think that it is impossible to check the number after I changed it because this is a recursion. It will also not work for any control structures. I have tried doing event based thing to changed the boolValue.Value but that doesn’t actually solve the problem. It makes it even worse because now when I the table’s size changed from 1 to 0 and then to 1 superfast, the countDown function will get called on top of the previous call and this will mess up the count

Hmm, what I mean is, your variable t seems to contain a reference to a table, and the function module.checkNumber(t) simply counts the number of entries in t, and returns true when there are 1 or more… or false if zero. Correct?

So it seems that something, somewhere else in the code, changes the content of that table t. Whenever that happens, that is when you should trigger the check.

So if somewhere else an entry is removed from table t, this should be done in a new function instead, that also does the check using module.CheckNumber(t) and sets boolValue.Value if the table is empty.

That way, if something adds a new entry to the table afterwards, it doesn’t matter because the game is already set to ‘ended’.

And the ‘ended’ flag can then be used to stop the loop with the timer.

I didn’t open your program, but is something like this possible?

I would need to fire a event from another place and there should be a thing in the countDown function that detects if the event is fired. I don’t know how to do that and I don’t know if it is even possible for a event that is fired to be detected inside function.

It seems to me that technically, you could have some code in this ‘other place’ that sets boolValue.Value to false.

local function changedTheTable(table)
  if not module.checkNumber(t) then
    boolValue.Value = false
  end
end

But it might not be a very neat place for that code, or it happens in multiple places. Indeed then technically you could use an event, such as a bindableEvent. You could do something like:

local function changedTheTable(myTable)
  myBindableEvent:Fire(myTable)
end

Then you could change your earlier script like:

myBindableEvent.Event:Connect(function(myTable)
	if not module.checkNumber(myTable) then
		boolValue.Value = false
	end
end)

function module.countDown(n, t)
	if boolValue.Value then
		if n == 0 then
			stringValue.Value = "Success" 
		else
			stringValue.Value = "Intermission in " .. n - 1
			wait(1)
			module.countDown(n - 1, t)
		end
	else
		stringValue.Value = "Not enough players"
	end
end

It’s not a very elegant or computationally cheap solution but it solves the problem at hand. Since you are writing a module, which can be imported, the correct solution would probably be to import this module in the places that change the table, and call the ‘module.changedTable’ function from there. Or better, let the module handle all the operations on the table, by calling functions in the module.

But I wanted to show you at least how a RBX event approach could work, you would have to put a bindableEvent somewhere where both scripts can grab a reference to it, it’s not a good solution. When I said ‘let events be the trigger’ I didn’t mean neccessarily an RBX (RBXScriptSignal) event, I meant it like:

When something causes something somewhere else, let that be the trigger for the response. In general, if you find yourself writing a loop that checks for a change every x seconds, then you didn’t follow this guideline, and then there is probably a better solution out there.

There isn’t a table.Changed RBX event, so a solution like that is not possible without magic functions. So whenever something wants to change the content of table t, it will have to connect to your module somehow (so you don’t ever miss a temporarily empty table t), or directly change boolValue.Value (which is less clean).

Eventually, you may want to make table t an object, and associate functions to it to set and check the contents, allowing that object to trigger game status functions in a gameManager module (which seems to be what you are working on).

But for now the task at hand is first to see what is going wrong and what must be done to fix it, then you can worry about the best or cleanest way to do it, and refactor.

I hope I got right what you wanted to do, and I didn’t spellcheck the example code. Good luck with this problem though, it’s a good problem to learn from.

Im confused a bit by what you said but I actually fixed it by doing a For loop, which checks the size of the table every 0.1 seconds. I have updated my studio project now and it works like this: There are three tables: roomOne, roomTwo, and roomThree. Each table represents a room. When a player try to go to a room, the player gets inserted into that corresponding table. All three tables have a metastable which change the player position to the room they want to go to automatically when you __newindex a table. After that, the metatables also checks if roomTwo, which is the intermission room, has enough players to start the intermission countdown. If it does then set boolValue to true. And in another place, there is an event that detects every time the boolValue is changed. And it call the countDown function there is enough player aka if boolValue = true. The countDown function fires and that is the only function that changes boolValue to false. Let me know if you want the scripts.