def main(): out = StarLog(level=1, time=datetime.datetime.now, star="* Thread-0") out.comment("initialising server") # set up a shared matchmaking pool pool = MatchmakingPool() # listen for connections incoming on PORT: connections = Connection.iter_listen(HOST, PORT) out.comment(f"listening on port {PORT}...") for connection, address in connections: # repeatedly accept a new connection, and hand off to a new thread out.comment("new client connected: ", address) out.comment("starting a new thread to handle this client...") handler = threading.Thread(target=servant, args=(connection, pool)) handler.daemon = True # so that this thread exits when main exits handler.start()
def main(): out = StarLog(level=1 + DEBUG, timefn=lambda: f"Thread-0 {datetime.now()}") out.comment("initialising server", depth=-1) # set up a shared matchmaking pool pool = MatchmakingPool(num_players=NUM_PLAYERS, special_channels=SPECIAL_CHANNELS) # listen for connections incoming on PORT: try: # Host of "" allows all incoming connections on the chosen port connections = Connection.iter_listen(host="", port=DEFAULT_SERVER_PORT) out.comment(f"listening on port {DEFAULT_SERVER_PORT}...") for connection, address in connections: # repeatedly accept a new connection, and hand off to a new thread out.comment("new client connected: ", address) out.comment("starting a new thread to handle this client...") handler = threading.Thread(target=servant, args=(connection, pool)) handler.daemon = True # so that new thread exits when main exits handler.start() except KeyboardInterrupt: print() # end line out.comment("bye!")
def connect_and_play(player, name, channel, host, port, logfilename=None, out_function=None, print_state=True, use_debugboard=False, use_colour=False, use_unicode=False): """ Connect to and coordinate a game with a server, return a string describing the result. """ # Configure behaviour of this function depending on parameters: out = out_function if out_function else (lambda *_, **__: None) # no-op if print_state: def display_state(players_str, game): out("displaying game info:") out(players_str, depth=1) out(game, depth=1) else: def display_state(players, game): pass # Set up a connection with the server out("connecting to battleground", depth=-1) out("attempting to connect to the server...") server = Server.from_address(host, port) out("connection established!") # Wait for some matching players out("looking for a game", depth=-1) channel_str = f"channel '{channel}'" if channel else "open channel" out(f"submitting game request as '{name}' in {channel_str}...") server.send(M.PLAY, name=name, channel=channel) server.recv(M.OKAY) out("game request submitted.") out(f"waiting for opponents in {channel_str}...") out("(press ^C to stop waiting)") # (wait through some OKAY-OKAY msg exchanges until a GAME message comes--- # the server is asking if we are still here waiting, or have disconnected) gamemsg = server.recv(M.OKAY | M.GAME) while gamemsg['mtype'] is not M.GAME: server.send(M.OKAY) gamemsg = server.recv(M.OKAY | M.GAME) # when we get a game message, it's time to play! out("setting up game", depth=-1, clear=True) out("opponents found!") out("white player:", gamemsg['white']) out("black player:", gamemsg['black']) # Initialise the player out("initialising player", depth=-1) out("waiting for colour assignment...") initmsg = server.recv(M.INIT) out("playing as", initmsg['colour'], depth=1) out("initialising your player class...") player.init(initmsg['colour']) out("ready to play!") server.send(M.OKAY) # Set up a new game and display the initial state and players out("game start", depth=-1) players_str = format_players_str(gamemsg, player.colour) game = Game(logfilename=logfilename, debugboard=use_debugboard, colourboard=use_colour, unicodeboard=use_unicode) display_state(players_str, game) # Now wait for messages from the sever and respond accordingly while True: msg = server.recv(M.TURN | M.UPD8 | M.OVER | M.ERRO) if msg['mtype'] is M.TURN: # it's our turn! out("your turn!", depth=-1, clear=True) display_state(players_str, game) # decide on action and submit it to server action = player.action() server.send(M.ACTN, action=action) elif msg['mtype'] is M.UPD8: # someone made a move! colour = msg['colour'] action = msg['action'] # update our local state, out("receiving update", depth=-1, clear=True) game.update(colour, action) display_state(players_str, game) player.update(colour, action) # then notify server we are ready to continue: server.send(M.OKAY) elif msg['mtype'] is M.OVER: # the game ended! return msg['result'] elif msg['mtype'] is M.ERRO: # seems like the server encountered an error, but not # with our connection raise ServerEncounteredError(msg['reason'])
def connect_and_play(player, options, out): # SET UP SERVER CONNECTION out.section("connecting to battleground") # attempt to connect to the server... out.comment("attempting to connect to the server...") server = Server.from_address(options.host, options.port) out.comment("connection established!") # FIND A GAME # we would like to play a game! if options.channel: channel_str = f"channel '{options.channel}'" else: channel_str = "open channel" out.comment( f"submitting game request as '{options.name}' in {channel_str}...") server.send(M.PLAY, name=options.name, channel=options.channel) server.recv(M.OKAY) out.comment("game request submitted.") # wait for the server to find a game for us... out.comment(f"waiting for opponents in {channel_str}...") out.comment("(press ^C to stop waiting)") # (wait through some OKAY-OKAY msg exchanges until a GAME message comes--- # the server is asking if we are still here waiting, or have disconnected) gamemsg = server.recv(M.OKAY | M.GAME) while gamemsg['mtype'] is not M.GAME: server.send(M.OKAY) gamemsg = server.recv(M.OKAY | M.GAME) # when we get a game message, it's time to play! out.comment("opponents found!") out.comment("red player: ", gamemsg['red']) out.comment("green player:", gamemsg['green']) out.comment("blue player: ", gamemsg['blue']) # PLAY THE GAME # Set up a new Chexers game and initialise our player. game = Chexers(logfilename=options.logfile, debugboard=options.verbosity > 2) out.section("initialising player") out.comment("waiting for colour assignment...") initmsg = server.recv(M.INIT | M.ERRO) if initmsg['mtype'] is M.ERRO: erromsg = initmsg out.section("connection error") out.print(erromsg['reason']) return out.comment("playing as", initmsg['colour'], pad=1) out.comment("initialising your player class...") player.init(initmsg['colour']) out.comment("ready to play!") server.send(M.OKAY) players = format_players(gamemsg, player.colour) # Display the initial state of the game. out.section("game start", clear=True) out.comment("displaying game info:") out.comments(players, pad=1) out.comments(game, pad=1) # Now wait for messages from the sever and respond accordingly: while True: msg = server.recv(M.TURN | M.UPD8 | M.OVER | M.ERRO) if msg['mtype'] is M.TURN: # it's our turn! out.section("your turn!", clear=True) out.comment("displaying game info:") out.comments(players, pad=1) out.comments(game, pad=1) # decide on action and submit it to server action = player.action() server.send(M.ACTN, action=action) elif msg['mtype'] is M.UPD8: # someone made a move! colour = msg['colour'] action = msg['action'] # update our local state, out.section("receiving update", clear=True) game.update(colour, action) out.comment("displaying game info:") out.comments(players, pad=1) out.comments(game, pad=1) player.update(colour, action) # then notify server we are ready to continue: server.send(M.OKAY) elif msg['mtype'] is M.OVER: # the game ended! either legitmately or through some # game error (e.g. non-allowed move by us or opponent) out.section("game over!") out.print(msg['result']) break elif msg['mtype'] is M.ERRO: out.section("connection error") out.print(msg['reason']) break
def connect_and_play( player, name, channel, host, port, log_filename=None, out_function=None, print_state=True, use_debugboard=False, use_colour=False, use_unicode=False, ): """ Connect to and coordinate a game with a server, return a string describing the result. Parameters: * player -- Your player's wrapped object (supporting 'init', 'update' and 'action' methods). * name -- Your player's name on the server * channel -- The matchmaking channel string * host -- The server address * port -- The server port * log_filename -- If not None, log all game actions to this path. * print_state -- If True, print a picture of the board after each update. * use_debugboard -- If True, use a larger board during updates (if print_state is also True). * use_colour -- Use ANSI colour codes for output. * use_unicode -- Use unicode symbols for output. """ # Configure behaviour of this function depending on parameters: if print_state: def display_state(players_str, game): comment("displaying game info:") comment( _RENDER( game, message=players_str, use_debugboard=use_debugboard, use_colour=use_colour, use_unicode=use_unicode, ), depth=1, ) else: def display_state(players_str, game): pass # Set up a connection with the server comment("connecting to battleground", depth=-1) comment("attempting to connect to the server...") server = Server.from_address(host, port) comment("connection established!") # Wait for some matching players comment("looking for a game", depth=-1) channel_str = f"channel '{channel}'" if channel else "open channel" comment(f"submitting game request as '{name}' in {channel_str}...") server.send(M.PLAY, name=name, channel=channel) server.recv(M.OKAY) comment("game request submitted.") comment(f"waiting for opponents in {channel_str}...") comment("(press ^C to stop waiting)") # (wait through some OKAY-OKAY msg exchanges until a GAME message comes--- # the server is asking if we are still here waiting, or have disconnected) gamemsg = server.recv(M.OKAY | M.GAME) while gamemsg["mtype"] is not M.GAME: server.send(M.OKAY) gamemsg = server.recv(M.OKAY | M.GAME) # when we get a game message, it's time to play! comment("setting up game", depth=-1, clear=True) comment("opponents found!") for colour in COLOURS: comment(f"{colour} player:", gamemsg[colour]) # Initialise the player comment("initialising player", depth=-1) comment("waiting for colour assignment...") initmsg = server.recv(M.INIT) comment("playing as", initmsg["colour"], depth=1) comment("initialising your player class...") player.init(initmsg["colour"]) comment("ready to play!") server.send(M.OKAY) # Set up a new game and display the initial state and players comment("game start", depth=-1) players_str = format_players_str(gamemsg, player.colour) game = Game(log_filename) display_state(players_str, game) # Now wait for messages from the sever and respond accordingly while True: msg = server.recv(M.TURN | M.UPD8 | M.OVER | M.ERRO) if msg["mtype"] is M.TURN: # TODO: For simultaneous play, there's no need to display the # state again at the start of the turn... # comment("your turn!", depth=-1, clear=True) # display_state(players_str, game) # decide on action and submit it to server action = player.action() server.send(M.ACTN, action=action) elif msg["mtype"] is M.UPD8: player_action = msg["player_action"] opponent_action = msg["opponent_action"] comment("receiving update", depth=-1, clear=True) if player.colour == "upper": game.update( upper_action=player_action, lower_action=opponent_action, ) else: game.update( upper_action=opponent_action, lower_action=player_action, ) display_state(players_str, game) player.update( player_action=player_action, opponent_action=opponent_action, ) # then notify server we are ready to continue: server.send(M.OKAY) elif msg["mtype"] is M.OVER: # the game ended! return msg["result"] elif msg["mtype"] is M.ERRO: # seems like the server encountered an error, but not # with our connection raise ServerEncounteredError(msg["reason"])