I'm creating a Discord bot to play poker. I've a function wait_for_betting_to_end
which is defined like this:
def wait_for_betting_to_end():
while not_all_players_have_bet():
pass
I've a command poker
inside a cog
which contains the following code fragment:
@commands.command(name='poker')
async def poker(self, ctx):
self.game_started = True
# ...
await preflop()
wait_for_betting_to_end()
# ...
I've a Discord command bet
inside a cog
:
@commands.command(name='bet')
async def bet(self, ctx, amt):
if not self.game_started:
print("You're not playing a game.")
return
# does something that would make wait_for_betting_to_end stop
The problem is that the user is never able to run the bet
command while playing poker; the execution flow remains stuck in wait_for_betting_to_end
forever. While not playing, bet
correctly displays the error and exists.
How can I fix this?
The problem with your code is that you make an infite loop in your wait_for_betting_to_end()
function. This is a mistake stemming from the thought that discord.py uses multithreading to get its asynchronous functionality(I can guess that much looking at the tags), however it doesn't. asyncio
works in a single thread, where it does a task (like receiving a message, processing a command, etc) and does the next task on completion of that one, the power of asyncio stems from the ability to temporarily 'freeze' a task when no progress can be made (like waiting for a response or just plain sleeping) to continue on to the next task, and resuming the frozen task when it needs to. (I'm pretty sure 'freezing' is completely wrong terminology btw). Every command that your bot gets is handled in its own task. You making the infinite loop never release the poker task, so it's blocking the whole loop.
To fix your problem there are a few possibilities, like:
Instead of just infinitely looping, call await asyncio.sleep(0.1)
instead of pass
in your loop, this will allow your bot to get messages from discord in that time, and thus react to responses from your users. To stop your while loop you could use a self.value which you set to False
when it needs be(in your bet command) (maybe use something like a dictionary with games for this as you probably want to run different games at the same time).
I don't really work with cog so I can't confidently give you a worked out example how you could do your code (at least not without risking missing some thing that can easily be done with cogs). But this should put you on the right path I believe.