Example #1
0
async def play(websocket, game, player, connected):
    """
    Receive and process moves from a player.

    """
    async for message in websocket:
        # Parse a "play" event from the UI.
        event = json.loads(message)
        assert event["type"] == "play"
        column = event["column"]

        try:
            # Play the move.
            row = game.play(player, column)
        except RuntimeError as exc:
            # Send an "error" event if the move was illegal.
            await error(websocket, str(exc))
            continue

        # Send a "play" event to update the UI.
        event = {
            "type": "play",
            "player": player,
            "column": column,
            "row": row,
        }
        websockets.broadcast(connected, json.dumps(event))

        # If move is winning, send a "win" event.
        if game.winner is not None:
            event = {
                "type": "win",
                "player": game.winner,
            }
            websockets.broadcast(connected, json.dumps(event))
Example #2
0
 def _send_logs_update(self):
     websockets.broadcast(
         self._websockets,
         json.dumps({
             "messageType": "logs",
             "data": self._logs
         }))
Example #3
0
 def _send_effects_update(self):
     websockets.broadcast(
         self._websockets,
         json.dumps({
             "messageType": "effects",
             "data": list(self._effects.values())
         }))
Example #4
0
def run_consumer(shutdown_flag, clients, lock):
    print("Starting Kafka Consumer.")
    schema_registry_client = SchemaRegistryClient(
        {"url": "http://localhost:8081"})
    deserializer = AvroDeserializer(schema_registry_client)
    config = {
        "bootstrap.servers": "localhost:9092",
        "group.id": "dashboard-demo",
        "value.deserializer": deserializer
    }

    consumer = DeserializingConsumer(config)
    consumer.subscribe(["DASHBOARD"])

    while not shutdown_flag.done():
        msg = consumer.poll(0.2)

        if msg is None:
            print("Waiting...")
        elif msg.error():
            print(f"ERROR: {msg.error()}")
        else:
            value = msg.value()
            formatted = simplejson.dumps(value)
            print(f"Sending {formatted} to {clients}")

            with lock:
                websockets.broadcast(clients, formatted)

    print("Closing Kafka Consumer")
    consumer.close()
Example #5
0
 def _send_components_update(self):
     websockets.broadcast(
         self._websockets,
         json.dumps({
             "messageType":
             "components",
             "data":
             [asdict(component) for component in self._components.values()]
         }))
Example #6
0
 def changed_history(self, history: List[str]):
     """Update the history and send to clients."""
     self._history = history
     websockets.broadcast(
         self._websockets,
         json.dumps({
             "messageType": "history",
             "data": self._history
         }))
Example #7
0
async def leave(player):
    log(f'player {player.nickname} is leaving')
    if player.game is not None:
        if player.game.started:
            player.move.set_result('_SURRENDER')
        else:
            await leave_game(player)
    players.remove(player)
    websockets.broadcast(
        sockets, 'PLAYERS ' + ' '.join(player.nickname for player in players))
Example #8
0
async def broadcast(method, size, delay):
    """Broadcast messages at regular intervals."""
    load_average = 0
    time_average = 0
    pc1, pt1 = time.perf_counter_ns(), time.process_time_ns()
    await asyncio.sleep(delay)
    while True:
        print(f"clients = {len(CLIENTS)}")
        pc0, pt0 = time.perf_counter_ns(), time.process_time_ns()
        load_average = 0.9 * load_average + 0.1 * (pt0 - pt1) / (pc0 - pc1)
        print(f"load = {(pt0 - pt1) / (pc0 - pc1) * 100:.1f}% / "
              f"average = {load_average * 100:.1f}%, "
              f"late = {(pc0 - pc1 - delay * 1e9) / 1e6:.1f} ms")
        pc1, pt1 = pc0, pt0

        assert size > 20
        message = str(time.time_ns()).encode() + b" " + os.urandom(size - 20)

        if method == "default":
            websockets.broadcast(CLIENTS, message)
        elif method == "naive":
            # Since the loop can yield control, make a copy of CLIENTS
            # to avoid: RuntimeError: Set changed size during iteration
            for websocket in CLIENTS.copy():
                await send(websocket, message)
        elif method == "task":
            for websocket in CLIENTS:
                asyncio.create_task(send(websocket, message))
        elif method == "wait":
            if CLIENTS:  # asyncio.wait doesn't accept an empty list
                await asyncio.wait([
                    asyncio.create_task(send(websocket, message))
                    for websocket in CLIENTS
                ])
        elif method == "queue":
            for queue in CLIENTS:
                queue.put_nowait(message)
        elif method == "pubsub":
            PUBSUB.publish(message)
        else:
            raise NotImplementedError(f"unsupported method: {method}")

        pc2 = time.perf_counter_ns()
        wait = delay + (pc1 - pc2) / 1e9
        time_average = 0.9 * time_average + 0.1 * (pc2 - pc1)
        print(f"broadcast = {(pc2 - pc1) / 1e6:.1f}ms / "
              f"average = {time_average / 1e6:.1f}ms, "
              f"wait = {wait * 1e3:.1f}ms")
        await asyncio.sleep(wait)
        print()
Example #9
0
async def process_events():
    """Listen to events in Redis and process them."""
    redis = aioredis.from_url("redis://127.0.0.1:6379/1")
    pubsub = redis.pubsub()
    await pubsub.subscribe("events")
    async for message in pubsub.listen():
        if message["type"] != "message":
            continue
        payload = message["data"].decode()
        # Broadcast event to all users who have permissions to see it.
        event = json.loads(payload)
        recipients = (
            websocket for websocket, connection in CONNECTIONS.items()
            if event["content_type_id"] in connection["content_type_ids"])
        websockets.broadcast(recipients, payload)
Example #10
0
 def log_message(self, level: str, timestamp: float, origin: str,
                 message: str):
     self._logs.append({
         "level": level,
         "timestamp": timestamp,
         "origin": origin,
         "message": message,
     })
     while len(self._logs) > self.MAX_NOF_LOGS:
         self._logs.pop(0)
     websockets.broadcast(
         self._websockets,
         json.dumps({
             "messageType": "log-added",
             "data": self._logs[-1]
         }))
Example #11
0
async def reconnect(socket, message):
    player = None
    try:
        token = message[10:]
        filtered_players = [
            player for player in players if player.token == token
        ]
        if len(filtered_players) == 0: raise MyException('invalid token')
        player = filtered_players[0]
        player.socket = socket
        log(f'player {player.nickname} has reconnected (token: {token})')
        await socket.send(f'RECONNECT_SUCCESS')
        websockets.broadcast(
            sockets,
            'PLAYERS ' + ' '.join(player.nickname for player in players))
        await socket.send('GAMES ' + ' '.join(game.creator.nickname
                                              for game in games))
    except MyException as e:
        await socket.send(f'RECONNECT_ERROR {e}')
    finally:
        return player
Example #12
0
async def enter(socket, message):
    player = None
    try:
        nickname = message[6:]
        if not nickname.isalnum():
            raise MyException('nickname must be an alpha-numeric string')
        if nickname in (x.nickname for x in players):
            raise MyException('nickname already taken')
        token = generate_token()
        log(f'new player: {nickname} (token: {token})')
        player = Player(socket, nickname, token)
        players.append(player)
        await socket.send(f'ENTER_SUCCESS {token}')
        websockets.broadcast(
            sockets,
            'PLAYERS ' + ' '.join(player.nickname for player in players))
        await socket.send('GAMES ' + ' '.join(game.creator.nickname
                                              for game in games))
    except MyException as e:
        await socket.send(f'ENTER_ERROR {e}')
    finally:
        return player
Example #13
0
async def leave_game(player):
    if player.game is None: return
    log(f'{player.nickname} left {player.game.creator.nickname}\'s game')
    game = player.game
    player.game = None
    if player in game.spectators:
        game.spectators.remove(player)
    elif player in game.players:
        game.players.remove(player)
        player.is_ready = False
        if player == game.creator:
            websockets.broadcast((x.socket for x in game.players),
                                 'GAME_ABANDONED')
            for player in game.players:
                player.game = None
                player.is_ready = False
            games.remove(game)
            websockets.broadcast(
                sockets,
                'GAMES ' + ' '.join(game.creator.nickname for game in games))
        else:
            await game.broadcast_state()
    else:
        log(f'{player.nickname} not in players nor spectators')
Example #14
0
async def counter(websocket, path):
    try:
        # Register user
        USERS.add(websocket)
        websockets.broadcast(USERS, users_event())
        # Send current state to user
        await websocket.send(state_event())
        # Manage state changes
        async for message in websocket:
            data = json.loads(message)
            if data["action"] == "minus":
                STATE["value"] -= 1
                websockets.broadcast(USERS, state_event())
            elif data["action"] == "plus":
                STATE["value"] += 1
                websockets.broadcast(USERS, state_event())
            else:
                logging.error("unsupported event: %s", data)
    finally:
        # Unregister user
        USERS.remove(websocket)
        websockets.broadcast(USERS, users_event())
Example #15
0
 async def broadcast(self, msg):
     receivers = set(x for x in self.players
                     if not x.is_fake).union(self.spectators)
     websockets.broadcast((x.socket for x in receivers), msg)
Example #16
0
async def start_game(game):
    await game.start()
    games.remove(game)
    websockets.broadcast(
        sockets, 'GAMES ' + ' '.join(game.creator.nickname for game in games))
Example #17
0
 def send_command(self, data) -> None:
     websockets.broadcast(self._websockets, json.dumps(data))
Example #18
0
async def handler(socket, path):
    log('new connection')
    sockets.append(socket)
    try:
        player = None
        async for message in socket:
            if player is None:
                if message.startswith('ENTER '):
                    player = await enter(socket, message)
                elif message.startswith('RECONNECT '):
                    player = await reconnect(socket, message)
                elif message.startswith('_BOT '):
                    token = message[5:]
                    bot, game = next((bot, game) for (bot, game) in bots
                                     if bot.token == token)
                    bots.remove((bot, game))
                    player = Player(socket, bot.nickname, bot.token)
                    player.game = game
                    player.game.players.append(player)
                    player.is_ready = True
                    await player.game.broadcast_state()
            else:
                if message == 'LEAVE':
                    await leave(player)
                    player = None
                    break
                if message.startswith('CREATE_GAME'):
                    log(f'new game (created by {player.nickname})')
                    password = message[12:]
                    game = Game(player, password)
                    games.append(game)
                    player.game = game
                    await game.broadcast_state()
                    websockets.broadcast(
                        sockets, 'GAMES ' + ' '.join(game.creator.nickname
                                                     for game in games))
                elif message.startswith('JOIN_GAME '):
                    await join_game(player, message)
                elif message.startswith('LEAVE_GAME'):
                    await leave_game(player)
                elif message.startswith('READY'):
                    if player.game is None: continue
                    player.is_ready = not player.is_ready
                    await player.game.broadcast_state()
                    if len(player.game.players) > 1 and all(
                            x.is_ready for x in player.game.players):
                        asyncio.create_task(start_game(player.game))
                elif message.startswith('GAME_OPTIONS '):
                    if player.game is None: continue
                    if player != player.game.creator: continue
                    options = json.loads(message[13:])
                    player.game.set_options(options)
                    for p in player.game.players:
                        if not 'bot_' in p.nickname:
                            p.is_ready = False
                    await player.game.broadcast_state()
                elif message.startswith('ADD_BOT '):
                    if player.game is None: continue
                    if player != player.game.creator: continue
                    if len(player.game.players) > 6: continue
                    level = message[8:]
                    protocol = 'wss' if port == 443 else 'ws'
                    ssl_context = ssl.create_default_context(
                        ssl.Purpose.CLIENT_AUTH) if port == 443 else None
                    ws = await websockets.connect(
                        f'{protocol}://localhost:{port}', ssl=ssl_context)
                    bot = spawn_bot(ws, level, player.game)
                    bots.append((bot, player.game))
                    asyncio.create_task(bot.run())
                elif message == 'ROLL':
                    await socket.send('ROLL ' +
                                      ' '.join(map(str, player.hidden_dice)))
                elif message == 'GAME_STATE' and player.game is not None:
                    await socket.send('GAME_STATE ' + player.game.state())
                elif message == 'GAME_LOG' and player.game is not None and player.game.started:
                    await socket.send('GAME_LOG ' + player.game.log)
                elif message.startswith('BID ') or message.startswith(
                        'REVEAL ') or message.startswith('CHALLENGE'):
                    player.move.set_result(message)
    except websockets.ConnectionClosed:
        pass
    finally:
        sockets.remove(socket)
        if player is not None:
            log(f'lost connection with player {player.nickname}')