mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-25 14:13:21 -07:00
Jak 1: Initial checkin of Client. Removed the colon from the game name.
This commit is contained in:
168
worlds/jakanddaxter/Client.py
Normal file
168
worlds/jakanddaxter/Client.py
Normal file
@@ -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("<II", len(form), 10)
|
||||
self.socket.sendall(header + form.encode())
|
||||
logger.info("Sent Form: " + form)
|
||||
|
||||
def g_connect(self) -> 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()
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user