async def open_connection(host: str, port: int, streamch: SendCh[net.JSONStream]) -> None: """ open connection with server. 1. open connection 2. send on channel 3. wait for 'log in' message from server 4. close channel """ raw = await trio.open_tcp_stream(host, port) stream = net.JSONStream(raw) log.info(f"connection open {stream}") await streamch.send(stream) async def wait_for_login(stream: net.JSONStream) -> None: """ reads until the server says to log in """ resp = await stream.read() if resp != {"type": "log in"}: log.error(f"invalid resp {resp}") await wait_for_login(stream) log.debug(f"waiting for 'log in' request {stream}") await wait_for_login(stream) log.debug(f"closing streamch {stream}") await streamch.aclose()
async def spawn( connch: SendCh[trio.abc.Stream], playerch: RecvCh[Player], quitch: SendCh[Player], ) -> None: left, right = new_half_stream_pair() await connch.send(right) assert await left.read() == {"type": "log in"} await left.write({"type": "log in", "username": "******"}) assert await left.read() == { "type": "log in update", "state": "accepted" } # the initiator should spit the player out player = await playerch.receive() assert player.username == "first" assert player.stream == net.JSONStream(right) # player quits from the lobby or a sub, or anything that isn't the # initiator await quitch.send(player) left, right = new_half_stream_pair() await connch.send(right) assert await left.read() == {"type": "log in"} # notice how we use the same username. It shouldn't block, because # the other quitted await left.write({"type": "log in", "username": "******"}) assert await left.read() == { "type": "log in update", "state": "accepted" } player = await playerch.receive() assert player.username == "first" assert player.stream == net.JSONStream(right) await conn_sendch.aclose() await quit_sendch.aclose()
async def get_players(playerch: RecvCh[Player]) -> None: order = ["quick", "average", "slow", "late", "average2", "closing"] i = 0 async for player in playerch: assert player.username == order[i] assert player.stream == net.JSONStream( conns[order[i]][1]), f"{order[i]!r} stream differs" i += 1
async def initiate_conn( rawstream: trio.abc.Stream, playerch: SendCh[Player], usernameslk: Lockable[List[str]], ) -> None: log.debug("initiating stream") stream = net.JSONStream(rawstream) with trio.move_on_after(120) as cancel_scope: username = None try: log.debug("asking for log in infos") await stream.write({"type": "log in"}) log.debug("getting valid username") username = await get_username(stream, usernameslk) if username is None: return log.info("connection dropped") player = Player(stream, username) log.debug(f"{username!r} accepted") await stream.write({"type": "log in update", "state": "accepted"}) except net.ConnectionClosed as e: log.warning("connection dropped", exc_info=e) # remove username from the list of usernames if username is not None: async with usernameslk as usernames: try: usernames.remove(username) except ValueError: pass return log.info("conn initialized %s", player) if cancel_scope.cancelled_caught: return log.warning("initiating conn: dropping after timeout %s", stream) # copy the username, because once it's sent on the channel, I shouldn't # access player username = player.username await playerch.send(player) log.info("player '%s' sent", username)
async def test_read_chuncky_connection(objects): """ Make sure that JSON stream works when texts doesn't come in nicely """ with trio.move_on_after(1) as cancel_scope: a, stream_controlled = trio.testing.memory_stream_pair() stream_tested = net.JSONStream(a) string = '\n'.join(json.dumps(obj) for obj in objects) + '\n' async with trio.open_nursery() as n: n.start_soon(_send_string_by_chunks, stream_controlled, string) n.start_soon(_assert_reading, stream_tested, objects) assert cancel_scope.cancelled_caught is False
async def test_read_multiple_object_per_message(objects): """ Make sure that JSON stream can read multiple objects in one read (one message) """ with trio.move_on_after(1) as cancel_scope: a, stream_controlled = trio.testing.memory_stream_pair() stream_tested = net.JSONStream(a) string = '\n'.join(json.dumps(obj) for obj in objects) + '\n' async with trio.open_nursery() as n: n.start_soon(_assert_reading, stream_tested, objects) n.start_soon(stream_controlled.send_all, bytes(string, encoding='utf-8')) assert cancel_scope.cancelled_caught is False
async def accept_players(self, stream): """ Accepts players and puts them into self.players once they are ready for the game loop """ log.info("New connection") player = Player(net.JSONStream(stream)) try: log.debug("Waiting for player name") await player.get_username() except net.ConnectionClosed: log.info(f"{player} connection closed") except Exception as e: log.exception("Initiater crashed") else: await self.initiate_player(player)
def new_stream_player(username: str) -> Tuple[Player, Player]: left, right = trio.testing.memory_stream_pair() return ( Player(net.JSONStream(left), username), Player(net.JSONStream(right), username), )
def new_half_stream_pair() -> Tuple[net.JSONStream, trio.abc.Stream]: left, right = trio.testing.memory_stream_pair() client = net.JSONStream(left) return client, right
async def connect_to_server(self): log.debug("Connecting to server...") self.pdata.stream = net.JSONStream(await trio.open_tcp_stream( "localhost", PORT)) self.state = STATE_WAITING_INPUT log.info(f"Connected to server ({self.state})")
def new_stream_pair() -> Tuple[net.JSONStream, net.JSONStream]: left, right = trio.testing.memory_stream_pair() return net.JSONStream(left), net.JSONStream(right)