From 3de94fb2bc4284a2f7662c74cb9fbde2ed34761b Mon Sep 17 00:00:00 2001 From: massimilianodelliubaldini <8584296+massimilianodelliubaldini@users.noreply.github.com> Date: Thu, 25 Apr 2024 21:05:13 -0400 Subject: [PATCH] Jak 1: Initial checkin of Client. Removed the colon from the game name. --- worlds/jakanddaxter/Client.py | 168 ++++++++++++++++++ worlds/jakanddaxter/GameID.py | 2 +- worlds/jakanddaxter/__init__.py | 2 +- ...en_Jak and Daxter The Precursor Legacy.md} | 0 4 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 worlds/jakanddaxter/Client.py rename worlds/jakanddaxter/docs/{en_Jak And Daxter.md => en_Jak and Daxter The Precursor Legacy.md} (100%) diff --git a/worlds/jakanddaxter/Client.py b/worlds/jakanddaxter/Client.py new file mode 100644 index 0000000000..659f67bec7 --- /dev/null +++ b/worlds/jakanddaxter/Client.py @@ -0,0 +1,168 @@ +import time +import struct +import typing +import asyncio +from socket import socket, AF_INET, SOCK_STREAM + +import colorama + +import Utils +from GameID import jak1_name +from .locs import CellLocations as Cells, ScoutLocations as Flies +from CommonClient import ClientCommandProcessor, CommonContext, logger, server_loop, gui_enabled + + +class JakAndDaxterClientCommandProcessor(ClientCommandProcessor): + ctx: "JakAndDaxterContext" + + # 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). + # 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. + + +class JakAndDaxterReplClient: + ip: str + port: int + socket: socket + connected: bool = False + listening: bool = False + compiled: bool = False + + def __init__(self, ip: str = "127.0.0.1", port: int = 8181): + self.ip = ip + self.port = port + self.connected = self.g_connect() + if self.connected: + self.listening = self.g_listen() + if self.connected and self.listening: + self.compiled = self.g_compile() + + # This helper function formats and sends `form` as a command to the REPL. + # ALL commands to the REPL should be sent using this function. + def send_form(self, form: str) -> None: + header = struct.pack(" bool: + if not self.ip or not self.port: + return False + + self.socket = socket(AF_INET, SOCK_STREAM) + self.socket.connect((self.ip, self.port)) + time.sleep(1) + print(self.socket.recv(1024).decode()) + return True + + def g_listen(self) -> bool: + self.send_form("(lt)") + return True + + def g_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)") + + # 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))") + + # Start compilation. This is blocking, so nothing will happen until the REPL is done. + self.send_form("(mi)") + + # 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))") + + # 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 + + def g_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 + + # TODO - In ArchipelaGOAL, override the 'get-pickup event so that it doesn't give you the item, + # it just plays the victory animation. Then define a new event type like 'get-archipelago + # to actually give ourselves the item. See game-info.gc and target-handler.gc. + + def give_power_cell(self, ap_id: int) -> None: + 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) + "))") + + def give_scout_fly(self, ap_id: int) -> None: + fly_id = Flies.to_game_id(ap_id) + self.send_form("(send-event " + "*target* \'get-archipelago " + "(pickup-type buzzer) " + "(the float " + str(fly_id) + "))") + + +class JakAndDaxterContext(CommonContext): + tags = {"AP"} + game = jak1_name + items_handling = 0b111 # Full item handling + command_processor = JakAndDaxterClientCommandProcessor + repl: JakAndDaxterReplClient + + def __init__(self, server_address: typing.Optional[str], password: typing.Optional[str]) -> None: + self.repl = JakAndDaxterReplClient() + super().__init__(server_address, password) + + def on_package(self, cmd: str, args: dict): + if cmd == "": + pass + + def run_gui(self): + from kvui import GameManager + + class JakAndDaxterManager(GameManager): + logging_pairs = [ + ("Client", "Archipelago") + ] + base_title = "Jak and Daxter ArchipelaGOAL Client" + + self.ui = JakAndDaxterManager(self) + self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + + +def run_game(): + pass + + +async def main(): + Utils.init_logging("JakAndDaxterClient", exception_logger="Client") + + ctx = JakAndDaxterContext(None, None) + ctx.server_task = asyncio.create_task(server_loop(ctx), name="server loop") + + if gui_enabled: + ctx.run_gui() + ctx.run_cli() + + run_game() + + await ctx.exit_event.wait() + await ctx.shutdown() + + +if __name__ == "__main__": + colorama.init() + asyncio.run(main()) + colorama.deinit() \ No newline at end of file diff --git a/worlds/jakanddaxter/GameID.py b/worlds/jakanddaxter/GameID.py index 37100eca4b..555be696af 100644 --- a/worlds/jakanddaxter/GameID.py +++ b/worlds/jakanddaxter/GameID.py @@ -2,4 +2,4 @@ jak1_id = 741000000 # The name of the game. -jak1_name = "Jak and Daxter: The Precursor Legacy" +jak1_name = "Jak and Daxter The Precursor Legacy" diff --git a/worlds/jakanddaxter/__init__.py b/worlds/jakanddaxter/__init__.py index 0930aa3c15..e0985b7560 100644 --- a/worlds/jakanddaxter/__init__.py +++ b/worlds/jakanddaxter/__init__.py @@ -24,7 +24,7 @@ class JakAndDaxterWebWorld(WebWorld): class JakAndDaxterWorld(World): # ID, name, version - game: str = jak1_name + game = jak1_name data_version = 1 required_client_version = (0, 4, 5) diff --git a/worlds/jakanddaxter/docs/en_Jak And Daxter.md b/worlds/jakanddaxter/docs/en_Jak and Daxter The Precursor Legacy.md similarity index 100% rename from worlds/jakanddaxter/docs/en_Jak And Daxter.md rename to worlds/jakanddaxter/docs/en_Jak and Daxter The Precursor Legacy.md