mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-05-27 03:19:55 -07:00
Jak and Daxter: Replace Pymem, Add Linux Support (#5850)
* Replace pymem with PyMemoryEditor (nonworking) * Add back pymem for faster windows address searching. * Replace other uses of pymem, parameterize executable names. * Updated to add linux and potential MacOS support to launching gk and … (#84) * Updated to add linux and potential MacOS support to launching gk and goalc. Still needs tested on MacOS. * Switched to using x-terminal-emulator instead of trying to find gnome-terminal or konsole Made argument building for suprocessing goalc easier to read Fixed OS X support to use osascript instead of attempting to run Terminal directly * Changed Terminal usage to use Archipelago's Launh utility, which handles terminal launching for me for both linux and OS X * Added try/except to re-connect the memory process. The process file/id changes over time on linux, and this works to re-connect without needing to restart * Removed Unsetting env var in favor of reporting to the source authors * Putting PyMemoryEditor local. (#85) * Putting PyMemoryEditor local --------- Co-authored-by: massimilianodelliubaldini <8584296+massimilianodelliubaldini@users.noreply.github.com> * Fixing minor problems (#87) * Refactor away circular launcher import. * Push latest PyMemoryEditor scan utility (#91) Co-authored-by: Louis M <Louis M> * Remove Pymem, rely solely on PyMemoryEditor. Add konsole support. * Jak 1: Remove vendored copy of PME, update imports, requirements, and manifest. * Jak 1: Prevent server connect until game is properly setup. * Jak 1: reduce REPL/Compiler confusion, small updates to setup guide. * Write hack for Konsole on AppImage to avoid OpenSSL error. * Refactor LD_LIBRARY_PATH hack. * Update worlds/jakanddaxter/agents/memory_reader.py Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> * Update worlds/jakanddaxter/agents/memory_reader.py Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --------- Co-authored-by: Morgan <morgan07kelley@gmail.com> Co-authored-by: Louis M <prog@tioui.com> Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
3b1971be66
commit
f3389f5d8b
@@ -1,13 +1,14 @@
|
||||
import logging
|
||||
import random
|
||||
import struct
|
||||
import sys
|
||||
from typing import ByteString, Callable
|
||||
import json
|
||||
import pymem
|
||||
from pymem import pattern
|
||||
from pymem.exception import ProcessNotFound, ProcessError, MemoryReadError, WinAPIError
|
||||
from PyMemoryEditor import OpenProcess, ProcessNotFoundError, ProcessIDNotExistsError, ClosedProcess
|
||||
from dataclasses import dataclass
|
||||
|
||||
import Utils
|
||||
from ..game_id import jak1_gk
|
||||
from ..locs import (orb_locations as orbs,
|
||||
cell_locations as cells,
|
||||
scout_locations as flies,
|
||||
@@ -166,7 +167,7 @@ class JakAndDaxterMemoryReader:
|
||||
initiated_connect: bool = False
|
||||
|
||||
# The memory reader just needs the game running.
|
||||
gk_process: pymem.process = None
|
||||
gk_process: OpenProcess | None = None
|
||||
|
||||
location_outbox: list[int] = []
|
||||
outbox_index: int = 0
|
||||
@@ -227,8 +228,10 @@ class JakAndDaxterMemoryReader:
|
||||
|
||||
if self.connected:
|
||||
try:
|
||||
self.gk_process.read_bool(self.gk_process.base_address) # Ping to see if it's alive.
|
||||
except (ProcessError, MemoryReadError, WinAPIError):
|
||||
# TODO - When PyMemoryEditor issue #15 is resolved, swap out this line for the commented one.
|
||||
# self.gk_process.read_process_memory(0, bytes, 1) # Ping to see if it's alive.
|
||||
OpenProcess(process_name=jak1_gk)
|
||||
except (ProcessNotFoundError, ProcessIDNotExistsError, ClosedProcess):
|
||||
msg = (f"Error reading game memory! (Did the game crash?)\n"
|
||||
f"Please close all open windows and reopen the Jak and Daxter Client "
|
||||
f"from the Archipelago Launcher.\n"
|
||||
@@ -272,28 +275,35 @@ class JakAndDaxterMemoryReader:
|
||||
|
||||
async def connect(self):
|
||||
try:
|
||||
self.gk_process = pymem.Pymem("gk.exe") # The GOAL Kernel
|
||||
logger.debug("Found the gk process: " + str(self.gk_process.process_id))
|
||||
except ProcessNotFound:
|
||||
self.gk_process = OpenProcess(process_name=jak1_gk) # The GOAL Kernel
|
||||
logger.debug(f"Found the gk process: {self.gk_process.pid}")
|
||||
except ProcessNotFoundError:
|
||||
self.log_error(logger, "Could not find the game process.")
|
||||
self.connected = False
|
||||
return
|
||||
|
||||
# If we don't find the marker in the first loaded module, we've failed.
|
||||
modules = list(self.gk_process.list_modules())
|
||||
marker_address = pattern.pattern_scan_module(self.gk_process.process_handle, modules[0], self.marker)
|
||||
if marker_address:
|
||||
# At this address is another address that contains the struct we're looking for: the game's state.
|
||||
# From here we need to add the length in bytes for the marker and 4 bytes of padding,
|
||||
# and the struct address is 8 bytes long (it's an uint64).
|
||||
goal_pointer = marker_address + len(self.marker) + 4
|
||||
self.goal_address = int.from_bytes(self.gk_process.read_bytes(goal_pointer, sizeof_uint64),
|
||||
byteorder="little",
|
||||
signed=False)
|
||||
logger.debug("Found the archipelago memory address: " + str(self.goal_address))
|
||||
await self.verify_memory_version()
|
||||
if Utils.is_windows or Utils.is_linux:
|
||||
marker_addresses = list(self.gk_process.search_by_value(bytes, len(self.marker), self.marker,
|
||||
writeable_only=True))
|
||||
if len(marker_addresses) > 0:
|
||||
# If we don't find the marker in the first loaded module, we've failed.
|
||||
goal_pointer = marker_addresses[0] + len(self.marker) + 4
|
||||
|
||||
# At this address is another address that contains the struct we're looking for: the game's state.
|
||||
# From here we need to add the length in bytes for the marker and 4 bytes of padding,
|
||||
# and the struct address is 8 bytes long (it's an uint64).
|
||||
self.goal_address = int.from_bytes(
|
||||
self.gk_process.read_process_memory(goal_pointer, bytes, sizeof_uint64),
|
||||
byteorder="little",
|
||||
signed=False)
|
||||
logger.debug("Found the archipelago memory address: " + str(self.goal_address))
|
||||
await self.verify_memory_version()
|
||||
else:
|
||||
self.log_error(logger, "Could not find the Archipelago marker address!")
|
||||
self.connected = False
|
||||
|
||||
else:
|
||||
self.log_error(logger, "Could not find the Archipelago marker address!")
|
||||
self.log_error(logger, f"Unsupported operating system: {sys.platform}!")
|
||||
self.connected = False
|
||||
|
||||
async def verify_memory_version(self):
|
||||
@@ -309,8 +319,8 @@ class JakAndDaxterMemoryReader:
|
||||
self.log_success(logger, "The Memory Reader is ready!")
|
||||
self.connected = True
|
||||
else:
|
||||
raise MemoryReadError(memory_version_offset, sizeof_uint32)
|
||||
except (ProcessError, MemoryReadError, WinAPIError):
|
||||
raise Exception(memory_version_offset, sizeof_uint32)
|
||||
except (ProcessNotFoundError, ProcessIDNotExistsError, ClosedProcess, Exception):
|
||||
if memory_version is None:
|
||||
msg = (f"Could not find a version number in the OpenGOAL memory structure!\n"
|
||||
f" Expected Version: {str(expected_memory_version)}\n"
|
||||
@@ -336,7 +346,7 @@ class JakAndDaxterMemoryReader:
|
||||
self.connected = False
|
||||
|
||||
async def print_status(self):
|
||||
proc_id = str(self.gk_process.process_id) if self.gk_process else "None"
|
||||
proc_id = str(self.gk_process.pid) if self.gk_process else "None"
|
||||
last_loc = str(self.location_outbox[self.outbox_index - 1] if self.outbox_index else "None")
|
||||
msg = (f"Memory Reader Status:\n"
|
||||
f" Game process ID: {proc_id}\n"
|
||||
@@ -451,7 +461,7 @@ class JakAndDaxterMemoryReader:
|
||||
self.finished_game = True
|
||||
self.log_success(logger, "Congratulations! You finished the game!")
|
||||
|
||||
except (ProcessError, MemoryReadError, WinAPIError):
|
||||
except (ProcessNotFoundError, ProcessIDNotExistsError, ClosedProcess):
|
||||
msg = (f"Error reading game memory! (Did the game crash?)\n"
|
||||
f"Please close all open windows and reopen the Jak and Daxter Client "
|
||||
f"from the Archipelago Launcher.\n"
|
||||
@@ -467,7 +477,7 @@ class JakAndDaxterMemoryReader:
|
||||
|
||||
def read_goal_address(self, offset: int, length: int) -> int:
|
||||
return int.from_bytes(
|
||||
self.gk_process.read_bytes(self.goal_address + offset, length),
|
||||
self.gk_process.read_process_memory(self.goal_address + offset, bytes, length),
|
||||
byteorder="little",
|
||||
signed=False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user