From 36fdd014127c0ae6d791b9af15c767bab0b1085e Mon Sep 17 00:00:00 2001 From: Jozef Porubcin Date: Sun, 4 Jun 2023 11:59:36 -0400 Subject: [PATCH] Host progress --- README.md | 4 +- config.py | 2 +- lobby.py | 4 +- old/client.py | 211 ---------------------- old/main.py | 479 -------------------------------------------------- server.py | 9 +- tong-its.py | 73 ++++---- 7 files changed, 48 insertions(+), 734 deletions(-) delete mode 100644 old/client.py delete mode 100644 old/main.py diff --git a/README.md b/README.md index c8d5f26..7ba0d74 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,6 @@ TODO: -* Start making the actual game! +* Implement client player +* Implement Draw +* Fix broken client coro or something diff --git a/config.py b/config.py index ea147b0..164ba51 100644 --- a/config.py +++ b/config.py @@ -48,4 +48,4 @@ def __init__(self): self.SPADE = '\U00002663' self.HEART = '\U00002665' self.DIAMOND = '\U00002666' - self.SUITS = [ CLUB, SPADE, HEART, DIAMOND ] + self.SUITS = [ self.CLUB, self.SPADE, self.HEART, self.DIAMOND ] diff --git a/lobby.py b/lobby.py index 5850d4e..af28649 100644 --- a/lobby.py +++ b/lobby.py @@ -262,7 +262,7 @@ async def displayJoin(state_info): return # Check client names - async with asyncio.timeout(state_info.settings.DELAY): + async with asyncio.timeout(Settings().DELAY): try: while state_info.handle.client_names == None: await asyncio.sleep(0) @@ -333,7 +333,7 @@ async def menuState(state_info): state_info.handle = Host() # Create server object - state_info.handle.server = await asyncio.start_server(state_info.handle.handle_client, host=socket.gethostname(), backlog=state_info.settings.MAX_CLIENTS) + state_info.handle.server = await asyncio.start_server(state_info.handle.handle_client, host=socket.gethostname(), backlog=Settings().MAX_CLIENTS) # Save port for registration and shutdown state_info.handle.port = state_info.handle.server.sockets[0].getsockname()[1] diff --git a/old/client.py b/old/client.py deleted file mode 100644 index 5581f44..0000000 --- a/old/client.py +++ /dev/null @@ -1,211 +0,0 @@ -#!/usr/bin/env python3 - -import http.client -import json -import time -import socket -import os - -CATALOG_SERVER = 'catalog.cse.nd.edu:9097' -ENTRY_TYPE = 'Tong-its' -PING_INTERVAL = 60 -READ_BLOCK_SIZE = 1<<12 -NUM_PLAYERS = 3 - -class Client: - def __init__(self): - self.sock = None - self.addr = None - self.port = None - - - # Retrieves list of lobbies - def lookup(self): - - lobbies = list() - - # Get catalog - http_conn = http.client.HTTPConnection(CATALOG_SERVER) - http_conn.request('GET', '/query.json') - - # Parse catalog - catalog = json.loads(http_conn.getresponse().read()) - http_conn.close() - - # Iterate through catalog - for entry in catalog: - - # If the entry dict has the necessary keys - if all(key in entry for key in ('type', 'address', 'port', 'lastheardfrom', 'owner', 'num_players')): - - # If the entry is an open lobby - if entry['type'] == ENTRY_TYPE and entry['lastheardfrom'] >= time.time_ns() / 1000000000.0 - PING_INTERVAL and int(entry['num_players']) < NUM_PLAYERS: - - # Ensure entry is most recent entry of its kind - for lobby_entry in lobbies: - if lobby_entry['address'] == entry['address'] and lobby_entry['port'] == entry['port'] and lobby_entry['owner'] == entry['owner']: - if entry['lastheardfrom'] > lobby_entry['lastheardfrom']: - lobbies.remove(lobby_entry) - - if entry not in lobbies: - lobbies.append(entry) - - return lobbies - - - def connect(self, addr, port): - try: - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((addr, port)) - self.addr = addr - self.port = port - - return 1 - except: - self.disconnect() - - - def disconnect(self): - self.sock.close() - self.addr = None - self.port = None - - - def send_message(self, message): - try: - message_size = len(message.encode()).to_bytes(8, 'big') - self.sock.sendall(message_size + message.encode()) - return 1 - - except: - self.disconnect() - return 0 - - - def get_message(self): - try: - # Get message size - message_size = int.from_bytes(self.sock.recv(8), 'big') - - # Get message - message = '' - bytes_read = 0 - while bytes_read < message_size: - message += self.sock.recv(min(READ_BLOCK_SIZE, message_size - bytes_read)).decode() - bytes_read += min(READ_BLOCK_SIZE, message_size - bytes_read) - - return json.loads(message) - - except: - self.disconnect() - - - # Attempt to join a lobby and get player number - def join_lobby(self, lobby): - - # Try to connect to lobby host - if self.connect(lobby['address'], lobby['port']) == 0: - print('Unable to connect to lobby host\n\n') - return 0 - - # Send name to lobby host - message = str(json.dumps({'name': os.getlogin()})) - if self.send_message(message) == 0: - print('Lost connection with the host\n\n') - return 0 - - # Get player number from host - message = self.get_message() - if self.sock: - if 'player_num' in message: - return int(message['player_num']) - - # Catch-all - print('Unexpected error: could not get player number from host') - return 0 - - - def drawCard(self, option): - try: - self.send_message(json.dumps({'command': 'drawCard', 'option': option})) - response = self.get_message() - - return response - - except: - print('Failed to get response.') - self.disconnect() - - return None - - - def expose(self, cards): - try: - self.send_message(json.dumps({'command': 'expose', 'cards': cards})) - response = self.get_message() - - return response - - except: - print('Failed to get response.') - self.disconnect() - - return None - - - def draw(self, cards): - try: - self.send_message(json.dumps({'command': 'draw'})) - response = self.get_message() - - return response - - except: - print('Failed to get response.') - self.disconnect() - - return None - - - def challenge(self): - try: - self.send_message(json.dumps({'command': 'challenge'})) - response = self.get_message() - - return response - - except: - print('Failed to get response.') - self.disconnect() - - return None - - - def fold(self): - try: - self.send_message(json.dumps({'command': 'fold'})) - response = self.get_message() - - return response - - except: - print('Failed to get response.') - self.disconnect() - - return None - - - def discardCard(self, card): - try: - self.send_message(json.dumps({'command': 'discardCard', 'card': card})) - response = self.get_message() - - return response - - except: - print('Failed to get response.') - self.disconnect() - - return None - - diff --git a/old/main.py b/old/main.py deleted file mode 100644 index ddd5d74..0000000 --- a/old/main.py +++ /dev/null @@ -1,479 +0,0 @@ -#!/usr/bin/env python3 - -import os -import json -import random - -from server import Server -from client import Client - -### CONSTANTS ### -# # -NUM_PLAYERS = 3 - -STARTING_HAND_SIZE = 12 - -RANKS = [ - 'A', '2', '3', '4', '5', '6', '7', - '8', '9', '10', 'J', 'Q', 'K' - ] - -CLUB = '\U00002660' -SPADE = '\U00002663' -HEART = '\U00002665' -DIAMOND = '\U00002666' - -SUITS = [ CLUB, SPADE, HEART, DIAMOND ] -# # -################# - - -### OBJECTS ### -# # -class Card: - def __init__(self, rank, suit): - self.rank = rank - self.suit = suit - self.points = min(max(1, RANKS.index(self.rank)), 10) - - def decompose(self): - return (self.rank, self.suit) - - -class Deck: - def __init__(self): - self.deck = [Card(rank, suit) for rank in RANKS for suit in SUITS] - - def shuffle(self): - random.shuffle(self.deck) - - def draw(self): - if len(self.deck): - return self.deck.pop() - - -class Discard: - def __init__(self): - self.discard = list() - - def put(self, card): - if card: - self.discard.append(card) - return 1 - else: - return 0 - - def draw(self): - if len(self.discard): - return self.discard.pop() - - -class Player: - def __init__(self): - # Whether player acts as a server or a client - self.server = None - self.client = None - - # Server will have deck - self.deck = None - - # 1 or 2 or 3 - self.number = None - - # list of Card objects - self.hand = None - - - def setIdentity(self, identity=''): - if identity == 'SERVER': - self.server = Server() - self.client = None - elif identity == 'CLIENT': - self.client = Client() - self.server = None - else: - self.server = None - self.client = None - - - def composeHand(self, hand_data): - self.hand = [Card(card_data[0], card_data[1]) for card_data in hand_data] - - - # Display hand in a column or a row - def displayHand(self, option): - self.hand.sort(key=lambda x: x.suit) - self.hand.sort(key=lambda x: RANKS.index(x.rank)) - - if option == 'col': - for i in range(len(self.hand)): - print(f'{i:2d}: {self.hand[i].rank.rjust(2)}{self.hand[i].suit}') - - elif option == 'row': - if len(self.hand) > 0: - print(f'{self.hand[0].rank.rjust(2)}{self.hand[0].suit}', end='') - - for i in range(1, len(self.hand)): - print(f' {self.hand[i].rank.rjust(2)}{self.hand[i].suit}', end='') - - print() -# # -############### - - -### GLOBALS ### -# # -player = Player() - -# List of player names -names = list() - -# Index for names for whose turn it is -turn = None - -CURR_STATE = None - -# Tracks the last player to draw a card from the deck to settle draws -last_draw = None -# # -############### - - -def menu(): - global player, CURR_STATE - - print('q: quit\n0: host\n1: join\n') - choice = input('> ') - while all(option != choice for option in ('q', '0', '1')): - print('Invalid option\n') - return - - if choice == 'q': - CURR_STATE = 'QUIT' - - elif choice == '0': - # Set player identity to server - player.setIdentity('SERVER') - - CURR_STATE = 'HOST' - - elif choice == '1': - # Set player identity to client - player.setIdentity('CLIENT') - - CURR_STATE = 'JOIN' - - -# Host a lobby -def hostLobby(): - global player, names, CURR_STATE - - # Host lobby - names, ret_val = player.server.host_lobby() - - if ret_val == 0: - names = list() - - print('Failed to start game\n') - - # Reset identity to unknown - player.setIdentity() - CURR_STATE = 'MENU' - return - - CURR_STATE = 'SETUP' - - -# Join a lobby -def joinLobby(): - global player, names, CURR_STATE - - # Get lobbies - lobbies = player.client.lookup() - - # Print option select - print('b: back to menu') - print('r: refresh list') - for i in range(len(lobbies)): - print(str(i) + ': ' + lobbies[i]['owner'] + ' - ' + str(lobbies[i]['address']) + ':' + str(lobbies[i]['port']) + ' [' + str(lobbies[i]['num_players']) + '/' + str(NUM_PLAYERS) + ']') - - # Get option - choice = input('> ') - if not choice.isnumeric() or int(choice) >= len(lobbies): - if choice == 'b': - # Reset identity to unknown - player.setIdentity() - CURR_STATE = 'MENU' - return - - if choice == 'r': - return - - print('Invalid option\n') - return - - # Try to connect to the lobby of choice - player.number = player.client.join_lobby(lobbies[int(choice)]) - - if player.number < 1 or player.number >= NUM_PLAYERS: - return - - # Wait for game to start I guess - print('Waiting for players...') - message = player.client.get_message() - - if player.client.sock: - if 'ready' in message and message['ready'] == '1': - if 'names' in message: - names = message['names'] - CURR_STATE = 'SETUP' - return - - print('Unexpected error: I have no idea') - player.setIdentity() - CURR_STATE = 'MENU' - - -def setup(): - global player, names, turn, CURR_STATE - - if player.server: - # Set own player number - player.number = 0 - - # Set state to first player - turn = random.randrange(NUM_PLAYERS) - - # Broadcast first player - message = str(json.dumps({'turn': str(turn)})) - - if player.server.broadcast_message(message) == 0: - print('Failed to send a message to a player') - player.setIdentity() - CURR_STATE = 'MENU' - return - - # Deal the cards - player.server.deck = Deck() - player.server.discard = Discard() - player.server.deck.shuffle() - - hands = list() - for i in range(NUM_PLAYERS): - hands.append(list()) - - for i in range(STARTING_HAND_SIZE): - for j in range(NUM_PLAYERS): - hands[j].append(player.server.deck.draw().decompose()) - - # Send hands to players - for i in range(1, NUM_PLAYERS): - message = str(json.dumps({'hand': hands[i]})) - - if player.server.send_message(player.server.player_sockets[i], message) == 0: - print('Failed to send hand to ' + names[i]) - player.setIdentity() - CURR_STATE = 'MENU' - return - - # Set own hand - player.composeHand(hands[0]) - - elif player.client: - - # Receive who's first - message = player.client.get_message() - - if 'turn' in message: - turn = int(message['turn']) - - # Receive hand from server - hand_data = player.client.get_message() - - if 'hand' in hand_data: - player.composeHand(hand_data['hand']) - - else: - print('Unexpected error: player is neither server nor client in SETUP state') - player.setIdentity() - CURR_STATE = 'MENU' - return - - os.system('clear') - #print(names[turn] + ' goes first!') - CURR_STATE = 'PLAYER_TURN' - - -def drawPhase(): - global player, turn, CURR_STATE - - # Get deck and discard count - if player.server: - deck_size = len(player.server.deck.deck) - discard_size = len(player.server.discard.discard) - - if discard_size > 0: - discard_top = player.server.discard.discard[0].decompose() - else: - discard_top = (None, None) - - message = str(json.dumps({'deck_size': str(deck_size), 'discard_size': str(discard_size), 'discard_top': discard_top})) - - if player.server.broadcast_message(message) == 0: - print('Failed to send a message to a player') - player.setIdentity() - CURR_STATE = 'MENU' - return - - elif player.client: - message = player.client.get_message() - - if 'deck_size' in message and 'discard_size' in message: - deck_size = int(message['deck_size']) - discard_size = int(message['discard_size']) - discard_top = message['discard_top'] - - else: - print('Unexpected error: player is neither server nor client in SETUP state') - player.setIdentity() - CURR_STATE = 'MENU' - return - - # Base case - if deck_size == 0: - CURR_STATE = 'END' - return - - # Display deck and discard sizes - print(f'Deck ({deck_size})') - - if discard_size > 0: - print(f'Discard ({discard_size}): {discard_top[0]}{discard_top[1]}\n') - else: - print(f'Discard ({discard_size})') - - # Display hand - print() - player.displayHand('row') - print() - - # Display whose turn it is - if player.number == turn: - print('Your turn\n') - - actions = list() - # Display possible actions - if deck_size > 0: - print(f'{len(actions)}: Draw a card from the deck') - actions.append('drawCard deck') - - # CONTINUE ACTIONS HERE: EXPOSE, ETC - - - return actions - - else: - print(names[turn] + '\'s turn\n') - - return [] - - -def player_turn(): - global player, turn, CURR_STATE, last_draw - - actions = drawPhase() - - # Your turn - if len(actions) > 0: - # Get option - choice = input('> ') - while not choice.isnumeric() or int(choice) >= len(actions): - print('Invalid option\n') - choice = input('> ') - - if actions[int(choice)] == 'drawCard deck': - c = player.client.drawCard('deck') - player.hand.append(Card(c[0], c[1])) - - elif actions[int(choice)] == 'drawCard discard': - c = player.client.drawCard('discard') - player.hand.append(Card(c[0], c[1])) - - last_draw = player.name - - - # Client operations: - # drawCard(option) - Draw from deck or discard pile - # expose(cards) - Expose meld - # draw() - Propose a draw - # challenge() - Accept the draw - # fold() - Decline the draw - # discardCard(card) - Discard a card - - # Server responses: - # drawCard(option) - Returns a decomposed card on success, otherwise failure - # expose(cards) - Returns success, otherwise failure - # draw() - Returns success, otherwise failure - # challenge() - Returns success, otherwise failure - # fold() - Returns success, otherwise failure - # discardCard(card) - Returns success, otherwise failure - - while(True): - pass - - # Someone else's turn - elif len(actions) == 0: - # Wait for client - if player.server: - print('Waiting for client') - while(True): - pass - - # Wait for server - elif player.client: - print('Waiting for server') - while(True): - pass - - # Error, probably going back to MENU state - else: - return - - -def end(): - pass - - -def changeState(): - global CURR_STATE - - if CURR_STATE == 'MENU': - menu() - elif CURR_STATE == 'HOST': - hostLobby() - elif CURR_STATE == 'JOIN': - joinLobby() - elif CURR_STATE == 'SETUP': - setup() - elif CURR_STATE == 'PLAYER_TURN': - player_turn() - elif CURR_STATE == 'END': - end() - elif CURR_STATE == 'QUIT': - return - else: - return - - return 1 - - -def main(): - global CURR_STATE - - CURR_STATE = 'MENU' - while changeState(): - pass - - -### EXECUTION ### -main() diff --git a/server.py b/server.py index 7d0f9e1..a6d1b5a 100644 --- a/server.py +++ b/server.py @@ -45,12 +45,13 @@ def __init__(self, clients): # Private self.deck = self._init_deck() + # Public + self.order = self._init_order() # Mixed - self.players = self._init_players([(None, os.getlogin())]+clients) + self.players = self._init_players([(None, None, os.getlogin())]+clients) # Public self.discard = list() - self.order = self._init_order() self.last_draw = None self.end = False @@ -83,8 +84,8 @@ def _init_order(self): # Deal cards to players - def _init_players(self, clients) - players = [Player(clients[i][1]) for i in range(Constants().NUM_PLAYERS)] + def _init_players(self, clients): + players = [Player(clients[i][2]) for i in range(Constants().NUM_PLAYERS)] for _ in range(Constants().STARTING_HAND_SIZE): for i in self.order: players[i].hand.append(self.deck.pop()) diff --git a/tong-its.py b/tong-its.py index 24371fe..4660792 100644 --- a/tong-its.py +++ b/tong-its.py @@ -11,10 +11,10 @@ import asyncio # To send lists of cards as messages -from server import decompose, compose +from server import decompose, compose, Server # To simplify message sending and receiving -from config import send_message, get_message +from config import send_message, get_message, Constants # Send game state to players other than host @@ -33,7 +33,7 @@ async def send_gamestate(ret_val, server): # Send vague info if it's not the player's own info else: - await send_message(ret_val[i-1][1], {'score': server.players[j].score, 'name': server.players[j].name, 'num_cards': len(server.players[j].hand), 'melds': [decompose(meld) for meld in server.players[j].melds], 'can_draw': server.players[j].can_draw}) + await send_message(ret_val[i-1][1], {'score': server.players[j].score, 'name': server.players[j].name, 'num_cards': len(server.players[j].hand), 'melds': [decompose(meld) if meld else None for meld in server.players[j].melds], 'can_draw': server.players[j].can_draw}) # Display function for host @@ -44,7 +44,7 @@ def host_display(server): # Display client players's vague info in order print(f'{server.players[i].name}') print(f'Score: {server.players[i].score}') - print(f'Cards: {server.players[i].num_cards}') + print(f'Cards: {len(server.players[i].hand)}') # Display melds print(f'Melds:') @@ -60,7 +60,7 @@ def host_display(server): # Draw self print(f'{server.players[0].name}') print(f'Score: {server.players[0].score}') - print(f'Cards: {server.players[0].num_cards}') + print(f'Cards: {len(server.players[0].hand)}') # Display melds print(f'Melds:') @@ -102,7 +102,7 @@ async def main(ret_val): while not server.end: # Send gamestate - send_gamestate(ret_val, server) + await send_gamestate(ret_val, server) # Display game state os.system('clear') @@ -110,7 +110,7 @@ async def main(ret_val): # Wait until it's your turn while server.order[0] != 0: - print(f'{server.players[server.order[0]].name\'s turn...') + print(f'{server.players[server.order[0]].name}\'s turn...') # Wait for client player to send action message = await get_message(ret_val[server.order[0]-1][0]) @@ -220,7 +220,7 @@ async def main(ret_val): server.order = server.order[1:] + [server.order[0]] # Send gamestate - send_gamestate(ret_val, server) + await send_gamestate(ret_val, server) # Redraw display os.system('clear') @@ -263,7 +263,7 @@ async def main(ret_val): valid_meld = 0 # Check for a 3-of-a-kind - for card in server.player[0].hand: + for card in server.players[0].hand: if card.rank == server.discard[-1].rank: valid_meld += 1 if valid_meld == 2: @@ -273,7 +273,7 @@ async def main(ret_val): valid_meld = 1 # Check for a straight flush (discard is lowest rank) - for card in server.player[0].hand: + for card in server.players[0].hand: if card.suit == server.discard[-1].suit: if Constants().RANKS.index(card.rank) == Constants().RANKS.index(server.discard[-1].rank) + valid_meld: valid_meld += 1 @@ -284,7 +284,7 @@ async def main(ret_val): valid_meld = -1 # Check for a straight flush (discard is middle rank) - for card in server.player[0].hand: + for card in server.players[0].hand: if card.suit == server.discard[-1].suit: if Constants().RANKS.index(card.rank) == Constants().RANKS.index(server.discard[-1].rank) + valid_meld: valid_meld += 2 @@ -295,7 +295,7 @@ async def main(ret_val): valid_meld = -2 # Check for a straight flush (discard is lowest rank) - for card in server.player[0].hand: + for card in server.players[0].hand: if card.suit == server.discard[-1].suit: if Constants().RANKS.index(card.rank) == Constants().RANKS.index(server.discard[-1].rank) + valid_meld: valid_meld += 1 @@ -343,7 +343,7 @@ async def main(ret_val): # Display enumeration for card select print(' 0', end='') - for i in range(len(server.players[0].hand)): + for i in range(1, len(server.players[0].hand)): print(f' {str(i).rjust(3)}', end='') print() @@ -389,7 +389,7 @@ async def main(ret_val): while server.order[0] == 0: # Send gamestate - send_gamestate(ret_val, server) + await send_gamestate(ret_val, server) # Redraw display os.system('clear') @@ -403,9 +403,9 @@ async def main(ret_val): valid_meld = 0 # Check for a 3-of-a-kind - for card in server.player[0].hand: - if card != server.player[0].hand[i]: - if card.rank == server.player[0].hand[i].rank: + for card in server.players[0].hand: + if card != server.players[0].hand[i]: + if card.rank == server.players[0].hand[i].rank: valid_meld += 1 if valid_meld == 2: break @@ -414,10 +414,10 @@ async def main(ret_val): valid_meld = 1 # Check for a straight flush (i card is lowest rank) - for card in server.player[0].hand: - if card != server.player[0].hand[i]: - if card.suit == server.player[0].hand[i].suit: - if Constants().RANKS.index(card.rank) == Constants().RANKS.index(server.player[0].hand[i].rank) + valid_meld: + for card in server.players[0].hand: + if card != server.players[0].hand[i]: + if card.suit == server.players[0].hand[i].suit: + if Constants().RANKS.index(card.rank) == Constants().RANKS.index(server.players[0].hand[i].rank) + valid_meld: valid_meld += 1 if valid_meld == 3: break @@ -426,10 +426,10 @@ async def main(ret_val): valid_meld = -1 # Check for a straight flush (i card is middle rank) - for card in server.player[0].hand: - if card != server.player[0].hand[i]: - if card.suit == server.player[0].hand[i].suit: - if Constants().RANKS.index(card.rank) == Constants().RANKS.index(server.player[0].hand[i].rank) + valid_meld: + for card in server.players[0].hand: + if card != server.players[0].hand[i]: + if card.suit == server.players[0].hand[i].suit: + if Constants().RANKS.index(card.rank) == Constants().RANKS.index(server.players[0].hand[i].rank) + valid_meld: valid_meld += 2 if valid_meld == 3: break @@ -438,18 +438,18 @@ async def main(ret_val): valid_meld = -2 # Check for a straight flush (i card is lowest rank) - for card in server.player[0].hand: - if card != server.player[0].hand[i]: - if card.suit == server.player[0].hand[i].suit: - if Constants().RANKS.index(card.rank) == Constants().RANKS.index(server.player[0].hand[i].rank) + valid_meld: + for card in server.players[0].hand: + if card != server.players[0].hand[i]: + if card.suit == server.players[0].hand[i].suit: + if Constants().RANKS.index(card.rank) == Constants().RANKS.index(server.players[0].hand[i].rank) + valid_meld: valid_meld += 1 if valid_meld == 0: break - # If a valid meld was found - if valid_meld >= 0: - can_expose_meld = True - print('0: Expose a meld') + # If a valid meld was found + if valid_meld >= 0: + can_expose_meld = True + print('0: Expose a meld') print('d: Discard a card') @@ -519,7 +519,7 @@ async def main(ret_val): # Display enumeration for card select print(' 0', end='') - for i in range(len(server.players[0].hand)): + for i in range(1, len(server.players[0].hand)): print(f' {str(i).rjust(3)}', end='') print() @@ -556,9 +556,10 @@ async def main(ret_val): # Client elif type(ret_val) is tuple: - # + while True: + pass -if __name__ == 'main': +if __name__ == '__main__': ret_val = lobby.main() asyncio.run(main(ret_val))