I’m making a multiplayer game and I’m making the multiplayer its own package. In my server class, I have a broadcast method and a send_to_all_except method. The problem I’m having it that the messages are not being sent properly (the messages are not being received by the client(s)). Both methods are pretty much identical except for the client_websockets list so I’ll only put the code for broadcast here.
def broadcast(self, msg):
client_websockets = [client.ws for client in self.clients]
for ws in client_websockets:
asyncio.create_task(self._send(ws, msg))
I know that client_websockets is not empty when running it so that’s not the problem. This is how it is done in the websockets documentation.
The code for _send is very simple and is shown below.
async def _send(self, ws, msg):
try:
await ws.send(msg)
except websockets.ConnectionClosed:
self.clients.remove([client for client in self.clients if client.ws == ws])
I have tried using asyncio.create_task() as in the code above and asyncio.ensure_future() but neither work.
I asked this exact question on Stack Overflow, but I got no responses and a downvote. My questions always seem to be frowned upon on there. Any feedback on why that might be would be appreciated.
Sorry for late reply. Have been busy.
Thanks for the simplification. I will write a reproducible example soon so hopefully you could see what the problem is then.
Again, sorry for the delay but I have some new info.
While writing the example, I’ve discovered that the issue isn’t actually with the server, which I’ve thought this whole time. It is rather with how the client handles messages. I have the main logic in the main thread, and the message handler in a side thread. This is causing issues somewhere. Here’s the example:
Server:
import websockets
import asyncio
clients = []
async def broadcast(msg):
print(f"Broadcasting: {msg}")
for ws in clients:
await ws.send(msg)
async def proxy(websocket):
clients.append(websocket)
async for msg in websocket:
print(msg)
await broadcast(msg)
async def run():
async with websockets.serve(proxy, "127.0.0.1", 6000):
await asyncio.Future()
if __name__ == "__main__":
asyncio.run(run())
Client:
import websockets
import asyncio
from threading import Thread
async def proxy(websocket):
while True:
msg_to_send = input("Message: ")
await websocket.send(msg_to_send)
async def run():
async with websockets.connect("ws://127.0.0.1:6000") as websocket:
thread = Thread(target=start_websocket_thread, args=(websocket,))
await proxy(websocket)
async def websocket_handler(websocket):
async for msg in websocket:
print(f"Message Received: {msg}")
def start_websocket_thread(websocket):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(websocket_handler(websocket))
if __name__ == "__main__":
asyncio.run(run())
Note:
The websocket_handler function in the client has to be asynchronous.
If you want any other information, just let me know and I’ll try not to take 5-7 business days
No but seriously thanks for helping me with such inconsistent replies.
Okay, yeah, this is definitely a problem. Spawning a thread is fast, and then you close out the async with block, which will close the websocket. Is there a reason you can’t simply await websocket_handler(websocket) ? That would be the most normal and logical way to do it.
I’m using a thread because this package is for multiplayer games and I need the game loop running constantly (the game loop is in the proxy function). Now that I think about it, I could probably just have some code in the game loop to check for messages every frame. I’m not on my computer right now so I’ll check it out once I am. I got this thread solution from @elis.byberi in this post.
Yes, but rather than creating an event loop directly inside a thread, you set up an event loop that runs a coroutine, which then spawns a thread to create another event loop.