summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier Gayot <olivier.gayot@sigexec.com>2020-05-27 20:50:16 +0200
committerOlivier Gayot <olivier.gayot@sigexec.com>2020-05-28 01:00:57 +0200
commit05235c8df61b0cd6bf588ee15a924576cb10aa0a (patch)
tree820706b25287b1c877d47fb3e7dad18731d988c9
parent40d54a78a6cfb510bfca30d9ab5cdb01f7956548 (diff)
Don't use coroutines for functions sending notifications
Functions which generate notifications for clients were all making use of await and async. This is not great because if we add a notification somewhere, we need to change the function to a coroutine and update all invocations (recursively changing all functions to coroutines). Instead, we now add a task to the event loop whenever a notification needs to be generated. This allows to drop the await and async specifiers from mostly everywhere. On the downside, it means that if we send a notification to n clients, we have to register n tasks. Signed-off-by: Olivier Gayot <olivier.gayot@sigexec.com>
-rw-r--r--swiftstory/Client.py38
-rw-r--r--swiftstory/Game.py30
-rw-r--r--swiftstory/Player.py8
-rw-r--r--swiftstory/SwiftStory.py82
4 files changed, 80 insertions, 78 deletions
diff --git a/swiftstory/Client.py b/swiftstory/Client.py
index bc347f9..5116f51 100644
--- a/swiftstory/Client.py
+++ b/swiftstory/Client.py
@@ -1,3 +1,4 @@
+import asyncio
import websockets
from swiftstory.Status import error
@@ -12,7 +13,7 @@ class Client:
self.socket = socket
self.player = None
- async def join_game(self, game_name, lang):
+ def join_game(self, game_name, lang):
if self.game is not None:
return error('You are already in a game')
@@ -25,27 +26,27 @@ class Client:
if game is None:
return error('Invalid language')
- return await game.try_join(self)
+ return game.try_join(self)
- async def play_white_card(self, card_id):
+ def play_white_card(self, card_id):
if self.game is None:
return error('You have to join a game first')
- return await self.game.try_play_card(self.player, card_id)
+ return self.game.try_play_card(self.player, card_id)
- async def pick_black_card(self):
+ def pick_black_card(self):
if self.game is None:
return error('You have to join a game first')
- return await self.game.try_become_judge(self.player)
+ return self.game.try_become_judge(self.player)
- async def collect_cards(self):
+ def collect_cards(self):
if self.game is None:
error('You have to join a game first')
- return await self.game.try_collect_cards(self.player)
+ return self.game.try_collect_cards(self.player)
- async def designate_card(self, card_id):
+ def designate_card(self, card_id):
if self.game is None:
return error('You have to join a game first')
- return await self.game.try_designate_card(self.player, card_id)
+ return self.game.try_designate_card(self.player, card_id)
def view_player_cards(self):
if self.game is None:
@@ -62,12 +63,15 @@ class Client:
return error('You have to join a game first')
return self.game.try_view_black_card(self.player)
- async def send_notification(self, message):
- try:
- await self.socket.send(message)
- except websockets.exceptions.ConnectionClosed:
- print("Recipient has disconnected.")
+ def register_notification(self, message):
+ async def f():
+ try:
+ await self.socket.send(message)
+ except websockets.exceptions.ConnectionClosed:
+ print("Recipient has disconnected.")
- async def disconnect(self):
+ asyncio.create_task(f())
+
+ def disconnect(self):
if self.player is not None:
- await self.game.disconnect(self.player)
+ self.game.disconnect(self.player)
diff --git a/swiftstory/Game.py b/swiftstory/Game.py
index 1282c0e..93502bd 100644
--- a/swiftstory/Game.py
+++ b/swiftstory/Game.py
@@ -28,7 +28,7 @@ class Game:
random.shuffle(self.board.white_pick)
random.shuffle(self.board.black_pick)
- async def try_join(self, client):
+ def try_join(self, client):
if len(self.players) >= 10:
return error('too many players in this game')
@@ -52,7 +52,7 @@ class Game:
for p in self.players:
if p is not player:
- await p.send_notification({'op': 'player_joined_game'})
+ p.register_notification({'op': 'player_joined_game'})
cards = [(idx, desc) for idx, (_, desc) in player.cards.items()]
@@ -66,7 +66,7 @@ class Game:
return success({'cards': cards, 'game_state': state})
- async def try_become_judge(self, player):
+ def try_become_judge(self, player):
if self.state is not self.WAITING_NEW_JUDGE:
# TODO what if the judge has quit ?
return error('Someone is judge already')
@@ -78,12 +78,12 @@ class Game:
for p in self.players:
if p is not player:
- await p.send_notification({'op': 'judge_designed'})
+ p.register_notification({'op': 'judge_designed'})
return self.try_view_black_card(player)
- async def try_play_card(self, player, card_id):
+ def try_play_card(self, player, card_id):
if self.state is not self.WAITING_COLLECTION:
return error('Who asked you to play now ?!')
@@ -101,12 +101,12 @@ class Game:
self.board.play_card(player, card)
- await self.judge.send_notification({'op': 'card_played'})
+ self.judge.register_notification({'op': 'card_played'})
return success({'card_id': card_id})
- async def try_collect_cards(self, player):
+ def try_collect_cards(self, player):
if self.state is not self.WAITING_COLLECTION:
return error('Do you think it\'s the moment for colletion !?')
@@ -120,12 +120,12 @@ class Game:
for p in self.players:
if p is not player:
- await p.send_notification({'op': 'cards_collected'})
+ p.register_notification({'op': 'cards_collected'})
return self.try_view_played_cards(player)
- async def try_designate_card(self, player, card_id):
+ def try_designate_card(self, player, card_id):
if self.state is not self.WAITING_DESIGNATION:
return error('Not now, moron !')
@@ -143,7 +143,7 @@ class Game:
except IndexError:
return error('Invalid card')
- await winner.inc_score()
+ winner.inc_score()
# put the cards back in the deck
self.board.recycle_played_cards()
@@ -153,7 +153,7 @@ class Game:
if p.has_played:
idx = p.receive_card(self.board.pick_white_card())
- await p.send_notification({
+ p.register_notification({
'op': 'received_card',
'content': {
'card': {
@@ -168,7 +168,7 @@ class Game:
for p in self.players:
if p is not player:
- await p.send_notification({'op': 'judge_needed'})
+ p.register_notification({'op': 'judge_needed'})
self.state = self.WAITING_NEW_JUDGE
@@ -191,19 +191,19 @@ class Game:
return error('The black card has not been revealed yet')
- async def disconnect(self, player):
+ def disconnect(self, player):
player.client = None
if self.judge is player:
self.judge = None
for p in self.players:
- await p.send_notification({'op': 'judge_needed'})
+ p.register_notification({'op': 'judge_needed'})
for card, p in self.board.played_cards:
p.receive_card(card)
- await p.send_notification({
+ p.register_notification({
'op': 'received_card',
'content': {
'card': {
diff --git a/swiftstory/Player.py b/swiftstory/Player.py
index 786569c..7193b64 100644
--- a/swiftstory/Player.py
+++ b/swiftstory/Player.py
@@ -17,9 +17,9 @@ class Player:
def pop_card(self, card_id):
return self.cards.pop(card_id)
- async def inc_score(self):
+ def inc_score(self):
self.score += 1
- await self.send_notification({
+ self.register_notification({
'op': 'updated_score',
'content': self.score,
})
@@ -29,10 +29,10 @@ class Player:
self.next_idx += 1
return self.next_idx - 1
- async def send_notification(self, obj):
+ def register_notification(self, obj):
if self.client is None:
return
message = json.dumps({'type': 'notification', 'content': obj})
- await self.client.send_notification(message)
+ self.client.register_notification(message)
diff --git a/swiftstory/SwiftStory.py b/swiftstory/SwiftStory.py
index b252040..2aa1444 100644
--- a/swiftstory/SwiftStory.py
+++ b/swiftstory/SwiftStory.py
@@ -14,50 +14,48 @@ from swiftstory.Status import error
game_manager = swiftstory.GameManager.GameManager()
-async def message_received_handler(client, message):
+def message_received_handler(client, message):
try:
json_msg = json.loads(message)
except JSONDecodeError:
- res = error('badly formatted json')
- else:
- op = json_msg['op']
- if op == 'join_game':
- try:
- game_name = json_msg['game_name']
- except KeyError:
- res = error('field `game_name\' is required')
- else:
- lang = json_msg.get('lang')
- res = await client.join_game(game_name, lang)
- elif op == 'view_player_cards':
- res = client.view_player_cards()
- elif op == 'view_black_card':
- res = client.view_black_card()
- elif op == 'view_played_cards':
- res = client.view_played_cards()
- elif op == 'pick_black_card':
- res = await client.pick_black_card()
- elif op == 'designate_card':
- card_id = None
- try:
- card_id = int(json_msg['card_id'])
- except (KeyError, TypeError):
- pass
- finally:
- res = await client.designate_card(card_id)
- elif op == 'play_white_card':
- try:
- card_id = int(json_msg['card_id'])
- except KeyError:
- res = error('field `card_id\' is required')
- else:
- res = await client.play_white_card(card_id)
- elif op == 'collect_cards':
- res = await client.collect_cards()
+ return error('badly formatted json')
+
+ op = json_msg['op']
+ if op == 'join_game':
+ try:
+ game_name = json_msg['game_name']
+ except KeyError:
+ return error('field `game_name\' is required')
else:
- res = error('invalid command')
-
- await client.socket.send(res)
+ lang = json_msg.get('lang')
+ return client.join_game(game_name, lang)
+ elif op == 'view_player_cards':
+ return client.view_player_cards()
+ elif op == 'view_black_card':
+ return client.view_black_card()
+ elif op == 'view_played_cards':
+ return client.view_played_cards()
+ elif op == 'pick_black_card':
+ return client.pick_black_card()
+ elif op == 'designate_card':
+ card_id = None
+ try:
+ card_id = int(json_msg['card_id'])
+ except (KeyError, TypeError):
+ pass
+ finally:
+ return client.designate_card(card_id)
+ elif op == 'play_white_card':
+ try:
+ card_id = int(json_msg['card_id'])
+ except KeyError:
+ return error('field `card_id\' is required')
+ else:
+ return client.play_white_card(card_id)
+ elif op == 'collect_cards':
+ return client.collect_cards()
+ else:
+ return error('invalid command')
async def swiftstory(websocket, path):
@@ -65,9 +63,9 @@ async def swiftstory(websocket, path):
with contextlib.suppress(websockets.exceptions.ConnectionClosed):
async for message in client.socket:
- await message_received_handler(client, message)
+ await client.socket.send(message_received_handler(client, message))
- await client.disconnect()
+ client.disconnect()
def main():