From 75461d62a03c048e5327d5d659fd0d40de6eda4a Mon Sep 17 00:00:00 2001 From: massimilianodelliubaldini <8584296+massimilianodelliubaldini@users.noreply.github.com> Date: Mon, 29 Apr 2024 22:42:52 -0400 Subject: [PATCH] Jak 1: There's magic in the air... --- worlds/jakanddaxter/Client.py | 25 +++++++--- .../{Options.py => JakAndDaxterOptions.py} | 0 worlds/jakanddaxter/Regions.py | 2 +- worlds/jakanddaxter/Rules.py | 2 +- worlds/jakanddaxter/__init__.py | 2 +- worlds/jakanddaxter/client/MemoryReader.py | 49 ++++++++++++++----- worlds/jakanddaxter/client/ReplClient.py | 5 +- 7 files changed, 60 insertions(+), 25 deletions(-) rename worlds/jakanddaxter/{Options.py => JakAndDaxterOptions.py} (100%) diff --git a/worlds/jakanddaxter/Client.py b/worlds/jakanddaxter/Client.py index 9efbd4bb20..9d0edc8c19 100644 --- a/worlds/jakanddaxter/Client.py +++ b/worlds/jakanddaxter/Client.py @@ -6,9 +6,9 @@ import colorama import Utils from CommonClient import ClientCommandProcessor, CommonContext, logger, server_loop, gui_enabled -from .GameID import jak1_name -from .client.ReplClient import JakAndDaxterReplClient -from .client.MemoryReader import JakAndDaxterMemoryReader +from worlds.jakanddaxter.GameID import jak1_name +from worlds.jakanddaxter.client.ReplClient import JakAndDaxterReplClient +from worlds.jakanddaxter.client.MemoryReader import JakAndDaxterMemoryReader import ModuleUpdate ModuleUpdate.update() @@ -96,9 +96,16 @@ class JakAndDaxterContext(CommonContext): self.ui = JakAndDaxterManager(self) self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + async def server_auth(self, password_requested: bool = False): + if password_requested and not self.password: + await super(JakAndDaxterContext, self).server_auth(password_requested) + await self.get_username() + await self.send_connect() + def on_package(self, cmd: str, args: dict): if cmd == "ReceivedItems": for index, item in enumerate(args["items"], start=args["index"]): + logger.info(args) self.repl.item_inbox[index] = item async def ap_inform_location_checks(self, location_ids: typing.List[int]): @@ -109,12 +116,14 @@ class JakAndDaxterContext(CommonContext): create_task_log_exception(self.ap_inform_location_checks(location_ids)) async def run_repl_loop(self): - await self.repl.main_tick() - await asyncio.sleep(0.1) + while True: + await self.repl.main_tick() + await asyncio.sleep(0.1) async def run_memr_loop(self): - await self.memr.main_tick(self.on_locations) - await asyncio.sleep(0.1) + while True: + await self.memr.main_tick(self.on_locations) + await asyncio.sleep(0.1) async def main(): @@ -139,4 +148,4 @@ async def main(): def launch(): colorama.init() asyncio.run(main()) - colorama.deinit() \ No newline at end of file + colorama.deinit() diff --git a/worlds/jakanddaxter/Options.py b/worlds/jakanddaxter/JakAndDaxterOptions.py similarity index 100% rename from worlds/jakanddaxter/Options.py rename to worlds/jakanddaxter/JakAndDaxterOptions.py diff --git a/worlds/jakanddaxter/Regions.py b/worlds/jakanddaxter/Regions.py index c13b485418..fa4a6aa880 100644 --- a/worlds/jakanddaxter/Regions.py +++ b/worlds/jakanddaxter/Regions.py @@ -2,7 +2,7 @@ import typing from enum import Enum, auto from BaseClasses import MultiWorld, Region from .GameID import jak1_name -from .Options import JakAndDaxterOptions +from .JakAndDaxterOptions import JakAndDaxterOptions from .Locations import JakAndDaxterLocation, location_table from .locs import CellLocations as Cells, ScoutLocations as Scouts diff --git a/worlds/jakanddaxter/Rules.py b/worlds/jakanddaxter/Rules.py index 467953a697..6758b3c7ce 100644 --- a/worlds/jakanddaxter/Rules.py +++ b/worlds/jakanddaxter/Rules.py @@ -1,5 +1,5 @@ from BaseClasses import MultiWorld, CollectionState -from .Options import JakAndDaxterOptions +from .JakAndDaxterOptions import JakAndDaxterOptions from .Regions import Jak1Level, Jak1SubLevel, level_table, subLevel_table from .Locations import location_table as item_table from .locs import CellLocations as Cells, ScoutLocations as Scouts diff --git a/worlds/jakanddaxter/__init__.py b/worlds/jakanddaxter/__init__.py index a07798475b..92f5549f3a 100644 --- a/worlds/jakanddaxter/__init__.py +++ b/worlds/jakanddaxter/__init__.py @@ -1,6 +1,6 @@ from BaseClasses import Item, ItemClassification, Tutorial from .GameID import jak1_id, jak1_name -from .Options import JakAndDaxterOptions +from .JakAndDaxterOptions import JakAndDaxterOptions from .Items import JakAndDaxterItem from .Locations import JakAndDaxterLocation, location_table as item_table from .locs import CellLocations as Cells, ScoutLocations as Scouts, OrbLocations as Orbs diff --git a/worlds/jakanddaxter/client/MemoryReader.py b/worlds/jakanddaxter/client/MemoryReader.py index 2a3b32a259..1c06289dea 100644 --- a/worlds/jakanddaxter/client/MemoryReader.py +++ b/worlds/jakanddaxter/client/MemoryReader.py @@ -1,8 +1,9 @@ import typing -import subprocess import pymem from pymem import pattern from pymem.exception import ProcessNotFound + +from CommonClient import logger from worlds.jakanddaxter.locs import CellLocations as Cells, ScoutLocations as Flies # Some helpful constants. @@ -18,7 +19,7 @@ class JakAndDaxterMemoryReader: connected: bool = False marked: bool = False - process = None + process: pymem.process = None marker_address = None goal_address = None @@ -33,17 +34,20 @@ class JakAndDaxterMemoryReader: async def main_tick(self, location_callback: typing.Callable): self.read_memory() + location_callback(self.location_outbox) - # Checked Locations in game. Handle 1 location per tick. + # Checked Locations in game. Handle the entire outbox every tick until we're up to speed. if len(self.location_outbox) > self.outbox_index: - await location_callback(self.location_outbox[self.outbox_index]) + location_callback(self.location_outbox) self.outbox_index += 1 def connect(self) -> bool: try: self.process = pymem.Pymem("gk.exe") # The GOAL Kernel + logger.info("Found the gk process: " + str(self.process.process_id)) return True except ProcessNotFound: + logger.error("Could not find the gk process.") return False def find_marker(self) -> bool: @@ -59,19 +63,40 @@ class JakAndDaxterMemoryReader: self.goal_address = int.from_bytes(self.process.read_bytes(goal_pointer, 8), byteorder="little", signed=False) + logger.info("Found the archipelago memory address: " + str(self.goal_address)) return True + logger.error("Could not find the archipelago memory address.") return False def read_memory(self) -> typing.List[int]: - next_cell_index = int.from_bytes(self.process.read_bytes(self.goal_address, 8)) - next_buzzer_index = int.from_bytes(self.process.read_bytes(self.goal_address + next_buzzer_index_offset, 8)) - next_cell = int.from_bytes(self.process.read_bytes(self.goal_address + cells_offset + (next_cell_index * 4), 4)) - next_buzzer = int.from_bytes(self.process.read_bytes(self.goal_address + cells_offset + (next_buzzer_index * 4), 4)) + next_cell_index = int.from_bytes( + self.process.read_bytes(self.goal_address, 8), + byteorder="little", + signed=False) + next_buzzer_index = int.from_bytes( + self.process.read_bytes(self.goal_address + next_buzzer_index_offset, 8), + byteorder="little", + signed=False) - if next_cell not in self.location_outbox: - self.location_outbox.append(Cells.to_ap_id(next_cell)) - if next_buzzer not in self.location_outbox: - self.location_outbox.append(Flies.to_ap_id(next_buzzer)) + for k in range(0, next_cell_index): + next_cell = int.from_bytes( + self.process.read_bytes(self.goal_address + cells_offset + (k * 4), 4), + byteorder="little", + signed=False) + cell_ap_id = Cells.to_ap_id(next_cell) + if cell_ap_id not in self.location_outbox: + self.location_outbox.append(cell_ap_id) + logger.info("Checked power cell: " + str(next_cell)) + + for k in range(0, next_buzzer_index): + next_buzzer = int.from_bytes( + self.process.read_bytes(self.goal_address + buzzers_offset + (k * 4), 4), + byteorder="little", + signed=False) + buzzer_ap_id = Flies.to_ap_id(next_buzzer) + if buzzer_ap_id not in self.location_outbox: + self.location_outbox.append(buzzer_ap_id) + logger.info("Checked scout fly: " + str(next_buzzer)) return self.location_outbox diff --git a/worlds/jakanddaxter/client/ReplClient.py b/worlds/jakanddaxter/client/ReplClient.py index 7d39ac866b..695f78d1ef 100644 --- a/worlds/jakanddaxter/client/ReplClient.py +++ b/worlds/jakanddaxter/client/ReplClient.py @@ -54,7 +54,8 @@ class JakAndDaxterReplClient: time.sleep(1) logger.info(self.socket.recv(1024).decode()) return True - except ConnectionRefusedError: + except ConnectionRefusedError as e: + logger.error(e.strerror) return False def listen(self) -> bool: @@ -96,7 +97,7 @@ class JakAndDaxterReplClient: return True def receive_item(self): - ap_id = self.item_inbox[self.inbox_index]["item"] + ap_id = getattr(self.item_inbox[self.inbox_index], "item") # Determine the type of item to receive. if ap_id in range(jak1_id, jak1_id + Flies.fly_offset):