mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-25 02:23:37 -07:00
Jak 1: Simplified startup process, updated docs, prayed.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import typing
|
||||
import asyncio
|
||||
import colorama
|
||||
@@ -33,11 +35,10 @@ def create_task_log_exception(awaitable: typing.Awaitable) -> asyncio.Task:
|
||||
class JakAndDaxterClientCommandProcessor(ClientCommandProcessor):
|
||||
ctx: "JakAndDaxterContext"
|
||||
|
||||
# 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 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.
|
||||
# The command processor is not async and cannot use async tasks, so long-running operations
|
||||
# like the /repl connect command (which takes 10-15 seconds to compile the game) have to be requested
|
||||
# with user-initiated flags. The text client will hang while the operation runs, but at least we can
|
||||
# inform the user to wait. The flags are checked by the agents every main_tick.
|
||||
def _cmd_repl(self, *arguments: str):
|
||||
"""Sends a command to the OpenGOAL REPL. Arguments:
|
||||
- connect : connect the client to the REPL (goalc).
|
||||
@@ -45,7 +46,7 @@ class JakAndDaxterClientCommandProcessor(ClientCommandProcessor):
|
||||
if arguments:
|
||||
if arguments[0] == "connect":
|
||||
logger.info("This may take a bit... Wait for the success audio cue before continuing!")
|
||||
self.ctx.repl.user_connect = True # Will attempt to reconnect on next tick.
|
||||
self.ctx.repl.initiated_connect = True
|
||||
if arguments[0] == "status":
|
||||
self.ctx.repl.print_status()
|
||||
|
||||
@@ -55,7 +56,7 @@ class JakAndDaxterClientCommandProcessor(ClientCommandProcessor):
|
||||
- status : check the internal status of the Memory Reader."""
|
||||
if arguments:
|
||||
if arguments[0] == "connect":
|
||||
self.ctx.memr.connect()
|
||||
self.ctx.memr.initiated_connect = True
|
||||
if arguments[0] == "status":
|
||||
self.ctx.memr.print_status()
|
||||
|
||||
@@ -125,6 +126,55 @@ class JakAndDaxterContext(CommonContext):
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
|
||||
async def run_game(ctx: JakAndDaxterContext):
|
||||
exec_directory = ""
|
||||
try:
|
||||
exec_directory = Utils.get_settings()["jakanddaxter_options"]["exec_directory"]
|
||||
files_in_path = os.listdir(exec_directory)
|
||||
if ".git" in files_in_path:
|
||||
# Indicates the user is running from source, append expected subdirectory appropriately.
|
||||
exec_directory = os.path.join(exec_directory, "out", "build", "Release", "bin")
|
||||
else:
|
||||
# Indicates the user is running from the official launcher, a mod launcher, or otherwise.
|
||||
# We'll need to handle version numbers in the path somehow...
|
||||
exec_directory = os.path.join(exec_directory, "versions", "official")
|
||||
latest_version = list(reversed(os.listdir(exec_directory)))[0]
|
||||
exec_directory = os.path.join(exec_directory, str(latest_version))
|
||||
except FileNotFoundError:
|
||||
logger.error(f"Unable to locate directory {exec_directory}, "
|
||||
f"unable to locate game executable.")
|
||||
return
|
||||
except KeyError as e:
|
||||
logger.error(f"Hosts.yaml does not contain {e.args[0]}, "
|
||||
f"unable to locate game executable.")
|
||||
return
|
||||
|
||||
gk = os.path.join(exec_directory, "gk.exe")
|
||||
goalc = os.path.join(exec_directory, "goalc.exe")
|
||||
|
||||
# Don't mind all the arguments, they are exactly what you get when you run "task boot-game" or "task repl".
|
||||
await asyncio.create_subprocess_exec(
|
||||
gk,
|
||||
"-v", "--game jak1", "--", "-boot", "-fakeiso", "-debug",
|
||||
stderr=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stdin=subprocess.DEVNULL)
|
||||
|
||||
# You MUST launch goalc as a console application, so powershell/cmd/bash/etc is the program
|
||||
# and goalc is just an argument. It HAS to be this way.
|
||||
# TODO - Support other OS's.
|
||||
await asyncio.create_subprocess_exec(
|
||||
"powershell.exe",
|
||||
goalc, "--user-auto", "--game jak1")
|
||||
|
||||
# Auto connect the repl and memr agents. Sleep 5 because goalc takes just a little bit of time to load,
|
||||
# and it's not something we can await.
|
||||
logger.info("This may take a bit... Wait for the success audio cue before continuing!")
|
||||
await asyncio.sleep(5)
|
||||
ctx.repl.initiated_connect = True
|
||||
ctx.memr.initiated_connect = True
|
||||
|
||||
|
||||
async def main():
|
||||
Utils.init_logging("JakAndDaxterClient", exception_logger="Client")
|
||||
|
||||
@@ -138,6 +188,8 @@ async def main():
|
||||
ctx.run_gui()
|
||||
ctx.run_cli()
|
||||
|
||||
# Find and run the game (gk) and compiler/repl (goalc).
|
||||
await run_game(ctx)
|
||||
await ctx.exit_event.wait()
|
||||
await ctx.shutdown()
|
||||
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from Options import Toggle, PerGameCommonOptions
|
||||
|
||||
|
||||
class EnableScoutFlies(Toggle):
|
||||
"""Enable to include each Scout Fly as a check. Adds 112 checks to the pool."""
|
||||
display_name = "Enable Scout Flies"
|
||||
# class EnableScoutFlies(Toggle):
|
||||
# """Enable to include each Scout Fly as a check. Adds 112 checks to the pool."""
|
||||
# display_name = "Enable Scout Flies"
|
||||
|
||||
|
||||
# class EnablePrecursorOrbs(Toggle):
|
||||
# """Enable to include each Precursor Orb as a check. Adds 2000 checks to the pool."""
|
||||
# display_name = "Enable Precursor Orbs"
|
||||
|
||||
|
||||
@dataclass
|
||||
class JakAndDaxterOptions(PerGameCommonOptions):
|
||||
enable_scout_flies: EnableScoutFlies
|
||||
# enable_scout_flies: EnableScoutFlies
|
||||
# enable_precursor_orbs: EnablePrecursorOrbs
|
||||
pass
|
||||
|
||||
@@ -24,6 +24,15 @@ class JakAndDaxterWebWorld(WebWorld):
|
||||
|
||||
|
||||
class JakAndDaxterWorld(World):
|
||||
"""
|
||||
Jak and Daxter: The Precursor Legacy is a 2001 action platformer developed by Naughty Dog
|
||||
for the PlayStation 2. The game follows the eponymous protagonists, a young boy named Jak
|
||||
and his friend Daxter, who has been transformed into an "ottsel." With the help of Samos
|
||||
the Sage of Green Eco and his daughter Keira, the pair travel north in search of a cure for Daxter,
|
||||
discovering artifacts created by an ancient race known as the Precursors along the way. When the
|
||||
rogue sages Gol and Maia Acheron plan to flood the world with Dark Eco, they must stop their evil plan
|
||||
and save the world.
|
||||
"""
|
||||
# ID, name, version
|
||||
game = jak1_name
|
||||
data_version = 1
|
||||
|
||||
@@ -11,13 +11,20 @@ next_cell_index_offset = 0 # Each of these is an uint64, so 8 bytes.
|
||||
next_buzzer_index_offset = 8 # Each of these is an uint64, so 8 bytes.
|
||||
cells_offset = 16
|
||||
buzzers_offset = 420 # cells_offset + (sizeof uint32 * 101 cells) = 16 + (4 * 101)
|
||||
end_marker_offset = 868 # buzzers_offset + (sizeof uint32 * 112 flies) = 420 + (4 * 112)
|
||||
|
||||
|
||||
# buzzers_offset
|
||||
# + (sizeof uint32 * 112 flies) <-- The buzzers themselves.
|
||||
# + (sizeof uint8 * 116 tasks) <-- A "cells-received" array for the game to handle new ownership logic.
|
||||
# = 420 + (4 * 112) + (1 * 116)
|
||||
end_marker_offset = 984
|
||||
|
||||
|
||||
class JakAndDaxterMemoryReader:
|
||||
marker: typing.ByteString
|
||||
goal_address = None
|
||||
connected: bool = False
|
||||
initiated_connect: bool = False
|
||||
|
||||
# The memory reader just needs the game running.
|
||||
gk_process: pymem.process = None
|
||||
@@ -30,6 +37,10 @@ class JakAndDaxterMemoryReader:
|
||||
self.connect()
|
||||
|
||||
async def main_tick(self, location_callback: typing.Callable):
|
||||
if self.initiated_connect:
|
||||
await self.connect()
|
||||
self.initiated_connect = False
|
||||
|
||||
if self.connected:
|
||||
try:
|
||||
self.gk_process.read_bool(self.gk_process.base_address) # Ping to see if it's alive.
|
||||
@@ -48,7 +59,7 @@ class JakAndDaxterMemoryReader:
|
||||
location_callback(self.location_outbox)
|
||||
self.outbox_index += 1
|
||||
|
||||
def connect(self):
|
||||
async def connect(self):
|
||||
try:
|
||||
self.gk_process = pymem.Pymem("gk.exe") # The GOAL Kernel
|
||||
logger.info("Found the gk process: " + str(self.gk_process.process_id))
|
||||
|
||||
@@ -15,7 +15,7 @@ class JakAndDaxterReplClient:
|
||||
port: int
|
||||
sock: socket
|
||||
connected: bool = False
|
||||
user_connect: bool = False # Signals when user tells us to try reconnecting.
|
||||
initiated_connect: bool = False # Signals when user tells us to try reconnecting.
|
||||
|
||||
# The REPL client needs the REPL/compiler process running, but that process
|
||||
# also needs the game running. Therefore, the REPL client needs both running.
|
||||
@@ -31,9 +31,9 @@ class JakAndDaxterReplClient:
|
||||
self.connect()
|
||||
|
||||
async def main_tick(self):
|
||||
if self.user_connect:
|
||||
if self.initiated_connect:
|
||||
await self.connect()
|
||||
self.user_connect = False
|
||||
self.initiated_connect = False
|
||||
|
||||
if self.connected:
|
||||
try:
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
The [Player Options Page](../player-options) for this game contains
|
||||
all the options you need to configure and export a config file.
|
||||
|
||||
At this time, these options don't do anything. Scout Flies are always randomized, and Precursor Orbs
|
||||
At this time, Scout Flies are always randomized, and Precursor Orbs
|
||||
are never randomized.
|
||||
|
||||
## What does randomization do to this game?
|
||||
All 101 Power Cells and 112 Scout Flies are now Location Checks
|
||||
and may contain Items for different games as well as different Items from within Jak and Daxter.
|
||||
and may contain Items for different games, as well as different Items from within Jak and Daxter.
|
||||
|
||||
## What is the goal of the game once randomized?
|
||||
To complete the game, you must defeat the Gol and Maia and stop them from opening the Dark Eco silo.
|
||||
@@ -25,7 +25,7 @@ in their games, and those Items will be automatically sent to your game.
|
||||
|
||||
If you have completed all possible tasks available to you but still cannot progress, you may have to wait for
|
||||
another player to find enough of your game's Items to allow you to progress. If that does not apply,
|
||||
double check your spoiler log to make sure you have all the items you should have. If you don't,
|
||||
double-check your spoiler log to make sure you have all the items you should have. If you don't,
|
||||
you may have encountered a bug. Please see the options for bug reporting below.
|
||||
|
||||
## What happens when I pick up an item?
|
||||
@@ -40,19 +40,19 @@ inform you where you received the Item from, and which one it is. Your Item coun
|
||||
tick up. The pause menu will not say "Task Completed" below the selected Power Cell, but the icon will be "activated."
|
||||
|
||||
## I can't reach a certain area within an accessible region, how do I get there?
|
||||
Some areas are locked behind ownership of specific Power Cells. For example, you cannot access Misty Island
|
||||
Some areas are locked behind possession of specific Power Cells. For example, you cannot access Misty Island
|
||||
until you have the "Catch 200 Pounds of Fish" Power Cell. Keep in mind, your access to Misty Island is determined
|
||||
_through ownership of this specific Power Cell only,_ **not** _by you completing the Fishing minigame._
|
||||
_through possession of this specific Power Cell only,_ **not** _by you completing the Fishing minigame._
|
||||
|
||||
## I got soft-locked and can't leave, how do I get out of here?
|
||||
As stated before, some areas are locked behind ownership of specific Power Cells. But you may already be past
|
||||
As stated before, some areas are locked behind possession of specific Power Cells. But you may already be past
|
||||
a point-of-no-return preventing you from backtracking. One example is the Forbidden Jungle temple, where
|
||||
the elevator is locked at the bottom, and if you haven't unlocked the Blue Eco Switch, you cannot access
|
||||
the Plant Boss's room and escape.
|
||||
|
||||
In this scenario, you will need to open your menu and find the "Teleport Home" option. Selecting this option
|
||||
will instantly teleport you to the nearest Sage's Hut in the last hub area you were in... or always to
|
||||
the Green Sage's Hut, depending on the feasibility of the former option. This feature is a work in progress.
|
||||
In this scenario, you will need to open your menu and find the "Warp To Home" option at the bottom of the list.
|
||||
Selecting this option will instantly teleport you to Geyser Rock. From there, you can teleport back to the nearest
|
||||
sage's hut to continue your journey.
|
||||
|
||||
## I think I found a bug, where should I report it?
|
||||
Depending on the nature of the bug, there are a couple of different options.
|
||||
@@ -60,9 +60,11 @@ Depending on the nature of the bug, there are a couple of different options.
|
||||
* If you found a logical error in the randomizer, please create a new Issue
|
||||
[here.](https://github.com/ArchipelaGOAL/Archipelago/issues)
|
||||
* Use this page if:
|
||||
* For example, you are stuck on Geyser Rock because one of the four Geyser Rock Power Cells is not on Geyser Rock.
|
||||
* You are hard-locked from progressing. For example, you are stuck on Geyser Rock because one of the four
|
||||
Geyser Rock Power Cells is not on Geyser Rock.
|
||||
* The randomizer did not respect one of the Options you chose.
|
||||
* You see a mistake, typo, etc. on this webpage.
|
||||
* You see an error or stack trace appear on the text client.
|
||||
* Please upload your config file and spoiler log file in the Issue, so we can troubleshoot the problem.
|
||||
|
||||
* If you encountered an error in OpenGOAL, please create a new Issue
|
||||
@@ -72,4 +74,5 @@ Depending on the nature of the bug, there are a couple of different options.
|
||||
* You fail to send Items you find in the game to the Archipelago server.
|
||||
* You fail to receive Items the server sends to you.
|
||||
* Your game disconnects from the server and cannot reconnect.
|
||||
* You go looking for a game item that has already disappeared before you could reach it.
|
||||
* Please upload any log files that may have been generated.
|
||||
@@ -36,15 +36,16 @@ At this time, the only supported method of setup is through Manual Compilation.
|
||||
|
||||
- Open 3 Powershell windows. If you have VSCode, you can run 3 terminals to consolidate this process.
|
||||
- In the first window, navigate to the Archipelago folder using `cd` and run `python ./Launcher.py`.
|
||||
- In the second window, navigate to the ArchipelaGOAL folder and run `task extract`. This will prompt you to tell the mod where to find your ISO file to dump its cotnents. When that is done, run `task repl`.
|
||||
- In the second window, navigate to the ArchipelaGOAL folder and run `task extract`. This will prompt you to tell the mod where to find your ISO file to dump its contents. When that is done, run `task repl`.
|
||||
- In the third window, navigate to the ArchipelaGOAL folder and run `task boot-game`. At this point, Jak should be standing outside Samos's hut.
|
||||
- You can now close all these windows
|
||||
- In the Launcher, click Generate to create a new random seed. Save the resulting zip file.
|
||||
- In the Launcher, click Host to host the Archipelago server. It will prompt you for the location of that zip file.
|
||||
- Once the server is running, in the Launcher, find the Jak and Daxter Client and click it. You should see the `task repl` window begin to compile the game.
|
||||
- When it completes, you should hear the menu closing sound effect, and the Client window should appear.
|
||||
- Once the server is running, in the Launcher, find the Jak and Daxter Client and click it. You should see the command window begin to compile the game.
|
||||
- When it completes, you should hear the menu closing sound effect, and you should see the text client indicate that the two agents are ready to communicate with the game.
|
||||
- Connect the client to the Archipelago server and enter your slot name. Once this is done, the game should be ready to play. Talk to Samos to trigger the cutscene where he sends you to Geyser Rock, and off you go!
|
||||
|
||||
Once you complete the setup steps, you need to repeat most of them in order to launch a new game. You can skip a few steps:
|
||||
Once you complete the setup steps, you should only need to run the Launcher again to generate a game, host a server, or run the client and connect to a server.
|
||||
- You never need to download the zip copies of the projects again (unless there are updates).
|
||||
- You never need to dump your ISO again.
|
||||
- You never need to extract the ISO assets again.
|
||||
@@ -63,17 +64,16 @@ Offline play is untested at this time.
|
||||
|
||||
### Runtime Failures
|
||||
|
||||
- If the client window appears but no sound plays, you will need to enter the repl commands into the client to connect it to the game. These are, in order:
|
||||
- `/repl connect 127.0.0.1 8181`
|
||||
- `/repl listen`
|
||||
- `/repl compile`
|
||||
- Once these are done, you can enter `/repl verify` and you should hear the menu sound again.
|
||||
- If the client window appears but no sound plays, you will need to enter the following commands into the client to connect it to the game.
|
||||
- `/repl connect`
|
||||
- `/memr connect`
|
||||
- Once these are done, you can enter `/repl status` and `/memr status` to check that everything is connected and ready.
|
||||
|
||||
## Gameplay Troubleshooting
|
||||
|
||||
### Known Issues
|
||||
|
||||
- Needing to open so many windows to play the game is a massive pain, and I hope to streamline this process in the future.
|
||||
- I've streamlined the process of connecting the client's agents to the game, but they are temperamental and I am bad at asynchronous programming.
|
||||
- The game needs to run in debug mode in order to allow the repl to connect to it. At some point I want to make sure it can run in retail mode, or at least hide the debug text on screen and play the game's introductory cutscenes properly.
|
||||
- The client is currently not very robust and doesn't handle failures gracefully. This may result in items not being delivered to the game, or location checks not being delivered to the server.
|
||||
- The game relates tasks and power cells closely but separately. Some issues may result from having to tell the game to check for the power cells you own, rather than the tasks you completed.
|
||||
Reference in New Issue
Block a user