From 4cc571f9d545b2ec29fc6d1041fa9d5f6917c9da Mon Sep 17 00:00:00 2001 From: Midgard Date: Thu, 11 Mar 2021 04:21:47 +0100 Subject: [PATCH] Second commit --- mozaic_receptionist.py | 95 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/mozaic_receptionist.py b/mozaic_receptionist.py index 0996d3a..84d877a 100755 --- a/mozaic_receptionist.py +++ b/mozaic_receptionist.py @@ -8,6 +8,12 @@ import websocket import time +def first(iterable, default=None): + for x in iterable: + return x + return default + + # WS data to server: https://github.com/iasoon/pw3-moz2/blob/prototype/src/websocket.rs#L27 # WS data from server: https://github.com/iasoon/pw3-moz2/blob/prototype/src/lobby_manager.rs#L244 # HTTP: https://github.com/iasoon/pw3-moz2/blob/prototype/src/main.rs#L451 @@ -32,6 +38,7 @@ class Lobby: self.own_player_id = None self.players = None + self.proposals = None @property @@ -43,6 +50,10 @@ class Lobby: return ("https://" if self.tls else "http://") + self.url_base + f"/api/lobbies/{self.lobby_id}" + def get_player_by_name(self, name): + return first(p["id"] for p in self.players.values() if p["name"] == name) + + def open_websocket(self, on_message, on_error, **kwargs): def on_open(ws): @@ -64,6 +75,13 @@ class Lobby: int(player_id): data for player_id, data in data["data"]["players"].items() } + self.proposals = data["data"]["proposals"] + + elif data["type"] == "playerData": + self.players[data["data"]["id"]] = data["data"] + + elif data["type"] == "proposalData": + self.proposals[data["data"]["id"]] = data["data"] on_message(ws, data) @@ -73,7 +91,8 @@ class Lobby: ) self.thread = threading.Thread( - target=self.ws.run_forever + target=self.ws.run_forever, + daemon=True ) self.thread.start() @@ -99,10 +118,21 @@ class Lobby: self.own_player_id = own_player_data["id"] + def create_proposal(self, map_name, max_turns, players): + self._post(f"/proposals", { + "config": { "mapName": map_name, "maxTurns": max_turns }, + "players": players + }) + + def accept_proposal(self, proposal_id): self._post(f"/proposals/{proposal_id}/accept", {"status": "Accepted"}) + def start_proposal(self, proposal_id): + self._post(f"/proposals/{proposal_id}/start", None) + + @staticmethod def from_lobby_url(url, bot_name, token) -> "Lobby": """ @@ -118,12 +148,17 @@ class Lobby: return Lobby(lobby_id, bot_name, token, url_base, tls) -def on_error(ws, error): - __import__("pprint").pprint(error) - -if __name__ == "__main__": +def main(): import sys + + if len(sys.argv) < 4 or sys.argv[1] in ("--help", "-h"): + print("MOZAIC receptionist -- manage MOZAIC matches from the comfort of your terminal", + file=sys.stderr) + print(f"Usage: {sys.argv[0]} ", file=sys.stderr) + return + + lobby_url = sys.argv[1] bot_token = sys.argv[2] bot_name = " ".join(sys.argv[3:]) @@ -152,14 +187,56 @@ if __name__ == "__main__": if can_answer(proposal) and owner_participates(proposal): proposal_id = proposal["id"] owner_name = lobby.players[proposal["owner_id"]]["name"] - print(f"Accepting proposal from {owner_name}") lobby.accept_proposal(proposal_id) + print(f"[Note: accepted proposal from {owner_name}]") + + def start_match_if_possible(proposal): + if lobby.own_player_id == proposal["owner_id"] and proposal["status"] == "pending" and all( + ( + p["status"] == "Accepted" and + # Everyone's bot must be connected + lobby.players[p["player_id"]]["client_connected"] + ) for p in proposal["players"] + ): + lobby.start_proposal(proposal["id"]) + print(f"[Note: started match]") def on_message(ws, msg): if msg["type"] == "proposalData": accept_if_possible_and_owner_participates(msg["data"]) - elif msg["type"] == "lobbyState": - for proposal in msg["data"]["proposals"].values(): + start_match_if_possible(msg["data"]) + + if msg["type"] == "lobbyState": + for proposal in lobby.proposals.values(): accept_if_possible_and_owner_participates(proposal) - lobby.open_websocket(on_message, on_error) + # Be prepared to start a match as soon as the last bot comes online + if msg["type"] in ("lobbyState", "playerData"): + for proposal in lobby.proposals.values(): + start_match_if_possible(proposal) + + lobby.open_websocket(on_message, print) + + try: + print("Note: auto-accepting and auto-starting in the background.") + while True: + print("To create new game, press enter.") + input() + print(" NEW GAME") + print() + map_name = input(" Map> ") + # FIXME Race: player data may not be received yet + print(" P1> " + lobby.players[lobby.own_player_id]["name"]) + + p2 = lobby.get_player_by_name(input(" P2> ")) + while not p2: + print("User not found") + p2 = lobby.get_player_by_name(input(" P2> ")) + + lobby.create_proposal(map_name, 500, [lobby.own_player_id, p2]) + except (KeyboardInterrupt, EOFError): + print(" Quitting") + + +if __name__ == "__main__": + main()