From f0659e36db3a603abcd40ff385f721c4309ab1e3 Mon Sep 17 00:00:00 2001 From: massimilianodelliubaldini <8584296+massimilianodelliubaldini@users.noreply.github.com> Date: Wed, 1 May 2024 17:35:52 -0400 Subject: [PATCH] Jak 1: Make the REPL a little more verbose, easier to debug. --- worlds/jakanddaxter/Client.py | 2 +- worlds/jakanddaxter/client/ReplClient.py | 130 +++++++++++++++-------- 2 files changed, 86 insertions(+), 46 deletions(-) diff --git a/worlds/jakanddaxter/Client.py b/worlds/jakanddaxter/Client.py index 9d0edc8c19..f3c2ef6187 100644 --- a/worlds/jakanddaxter/Client.py +++ b/worlds/jakanddaxter/Client.py @@ -36,7 +36,7 @@ class JakAndDaxterClientCommandProcessor(ClientCommandProcessor): # TODO - Clean up commands related to the REPL, make them more user friendly. # The REPL has a specific order of operations it needs to do in order to process our input: # 1. Connect (we need to open a socket connection on ip/port to the REPL). - # 2. Listen (have the REPL compiler connect and listen on the game's REPL server's socket). + # 2. Listen (have the REPL compiler connect and listen on the game's internal socket). # 3. Compile (have the REPL compiler compile the game into object code it can run). # All 3 need to be done, and in this order, for this to work. def _cmd_repl(self, *arguments: str): diff --git a/worlds/jakanddaxter/client/ReplClient.py b/worlds/jakanddaxter/client/ReplClient.py index 695f78d1ef..9149950b6c 100644 --- a/worlds/jakanddaxter/client/ReplClient.py +++ b/worlds/jakanddaxter/client/ReplClient.py @@ -37,64 +37,94 @@ class JakAndDaxterReplClient: # This helper function formats and sends `form` as a command to the REPL. # ALL commands to the REPL should be sent using this function. - # TODO - this needs to block on receiving an acknowledgement from the REPL server. - # Problem is, it doesn't ack anything right now. So we need that to happen first. - def send_form(self, form: str) -> None: + # TODO - this blocks on receiving an acknowledgement from the REPL server. But it doesn't print + # any log info in the meantime. Is that a problem? + def send_form(self, form: str, print_ok: bool = True) -> bool: header = struct.pack(" bool: + logger.info("Connecting to the OpenGOAL REPL...") if not self.ip or not self.port: + logger.error(f"Unable to connect: IP address \"{self.ip}\" or port \"{self.port}\" was not provided.") return False try: self.socket = socket(AF_INET, SOCK_STREAM) self.socket.connect((self.ip, self.port)) time.sleep(1) - logger.info(self.socket.recv(1024).decode()) - return True + welcome_message = self.socket.recv(1024).decode() + + # Should be the OpenGOAL welcome message (ignore version number). + if "Connected to OpenGOAL" and "nREPL!" in welcome_message: + logger.info(welcome_message) + return True + else: + logger.error(f"Unable to connect: unexpected welcome message \"{welcome_message}\"") + return False except ConnectionRefusedError as e: - logger.error(e.strerror) + logger.error(f"Unable to connect: {e.strerror}") return False def listen(self) -> bool: - self.send_form("(lt)") - return True + logger.info("Listening for the game...") + return self.send_form("(lt)") def compile(self) -> bool: - # Show this visual cue when compilation is started. - # It's the version number of the OpenGOAL Compiler. - self.send_form("(set! *debug-segment* #t)") + logger.info("Compiling the game... Wait for the success sound before continuing!") + ok_count = 0 + try: + # Show this visual cue when compilation is started. + # It's the version number of the OpenGOAL Compiler. + if self.send_form("(set! *debug-segment* #t)", print_ok=False): + ok_count += 1 - # Play this audio cue when compilation is started. - # It's the sound you hear when you press START + CIRCLE to open the Options menu. - self.send_form("(dotimes (i 1) " - "(sound-play-by-name " - "(static-sound-name \"start-options\") " - "(new-sound-id) 1024 0 0 (sound-group sfx) #t))") + # Play this audio cue when compilation is started. + # It's the sound you hear when you press START + CIRCLE to open the Options menu. + if self.send_form("(dotimes (i 1) " + "(sound-play-by-name " + "(static-sound-name \"start-options\") " + "(new-sound-id) 1024 0 0 (sound-group sfx) #t))", print_ok=False): + ok_count += 1 - # Start compilation. This is blocking, so nothing will happen until the REPL is done. - self.send_form("(mi)") + # Start compilation. This is blocking, so nothing will happen until the REPL is done. + if self.send_form("(mi)", print_ok=False): + ok_count += 1 - # Play this audio cue when compilation is complete. - # It's the sound you hear when you press START + START to close the Options menu. - self.send_form("(dotimes (i 1) " - "(sound-play-by-name " - "(static-sound-name \"menu-close\") " - "(new-sound-id) 1024 0 0 (sound-group sfx) #t))") + # Play this audio cue when compilation is complete. + # It's the sound you hear when you press START + START to close the Options menu. + if self.send_form("(dotimes (i 1) " + "(sound-play-by-name " + "(static-sound-name \"menu-close\") " + "(new-sound-id) 1024 0 0 (sound-group sfx) #t))", print_ok=False): + ok_count += 1 - # Disable cheat-mode and debug (close the visual cue). - self.send_form("(set! *cheat-mode* #f)") - # self.send_form("(set! *debug-segment* #f)") - return True + # Disable cheat-mode and debug (close the visual cue). + # self.send_form("(set! *debug-segment* #f)") + if self.send_form("(set! *cheat-mode* #f)"): + ok_count += 1 + + except: + logger.error(f"Unable to compile: commands were not sent properly.") + return False + + # Now wait until we see the success message... 5 times. + return ok_count == 5 def verify(self) -> bool: - self.send_form("(dotimes (i 1) " - "(sound-play-by-name " - "(static-sound-name \"menu-close\") " - "(new-sound-id) 1024 0 0 (sound-group sfx) #t))") - return True + logger.info("Verifying compilation... if you don't hear the success sound, try listening and compiling again!") + return self.send_form("(dotimes (i 1) " + "(sound-play-by-name " + "(static-sound-name \"menu-close\") " + "(new-sound-id) 1024 0 0 (sound-group sfx) #t))") def receive_item(self): ap_id = getattr(self.item_inbox[self.inbox_index], "item") @@ -109,16 +139,26 @@ class JakAndDaxterReplClient: elif ap_id > jak1_id + Orbs.orb_offset: pass # TODO - def receive_power_cell(self, ap_id: int) -> None: + def receive_power_cell(self, ap_id: int) -> bool: cell_id = Cells.to_game_id(ap_id) - self.send_form("(send-event " - "*target* \'get-archipelago " - "(pickup-type fuel-cell) " - "(the float " + str(cell_id) + "))") + ok = self.send_form("(send-event " + "*target* \'get-archipelago " + "(pickup-type fuel-cell) " + "(the float " + str(cell_id) + "))") + if ok: + logger.info(f"Received power cell {cell_id}!") + else: + logger.error(f"Unable to receive power cell {cell_id}!") + return ok - def receive_scout_fly(self, ap_id: int) -> None: + def receive_scout_fly(self, ap_id: int) -> bool: fly_id = Flies.to_game_id(ap_id) - self.send_form("(send-event " - "*target* \'get-archipelago " - "(pickup-type buzzer) " - "(the float " + str(fly_id) + "))") + ok = self.send_form("(send-event " + "*target* \'get-archipelago " + "(pickup-type buzzer) " + "(the float " + str(fly_id) + "))") + if ok: + logger.info(f"Received scout fly {fly_id}!") + else: + logger.error(f"Unable to receive scout fly {fly_id}!") + return ok