Jak & Daxter Client : queue game text messages to get items faster during release (#48)

* queue game text messages to write them during the main_tick function and empty the message queue faster during release

* wrap comment for code style character limit

Co-authored-by: massimilianodelliubaldini <8584296+massimilianodelliubaldini@users.noreply.github.com>

* remove useless blank line

Co-authored-by: massimilianodelliubaldini <8584296+massimilianodelliubaldini@users.noreply.github.com>

* whitespace code style

Co-authored-by: massimilianodelliubaldini <8584296+massimilianodelliubaldini@users.noreply.github.com>

* Move JsonMessageData dataclass outside of ReplClient class for code clarity

---------

Co-authored-by: massimilianodelliubaldini <8584296+massimilianodelliubaldini@users.noreply.github.com>
This commit is contained in:
Romain BERNARD
2024-09-10 02:55:00 +02:00
committed by GitHub
parent e6b58aa2be
commit 8922b1a5c9
2 changed files with 40 additions and 17 deletions

View File

@@ -160,31 +160,35 @@ class JakAndDaxterContext(CommonContext):
async def json_to_game_text(self, args: dict):
if "type" in args and args["type"] in {"ItemSend"}:
my_item_name = Optional[str]
my_item_finder = Optional[str]
their_item_name = Optional[str]
their_item_owner = Optional[str]
item = args["item"]
recipient = args["receiving"]
# Receiving an item from the server.
if self.slot_concerns_self(recipient):
self.repl.my_item_name = self.item_names.lookup_in_game(item.item)
my_item_name = self.item_names.lookup_in_game(item.item)
# Did we find it, or did someone else?
if self.slot_concerns_self(item.player):
self.repl.my_item_finder = "MYSELF"
my_item_finder = "MYSELF"
else:
self.repl.my_item_finder = self.player_names[item.player]
my_item_finder = self.player_names[item.player]
# Sending an item to the server.
if self.slot_concerns_self(item.player):
self.repl.their_item_name = self.item_names.lookup_in_slot(item.item, recipient)
their_item_name = self.item_names.lookup_in_slot(item.item, recipient)
# Does it belong to us, or to someone else?
if self.slot_concerns_self(recipient):
self.repl.their_item_owner = "MYSELF"
their_item_owner = "MYSELF"
else:
self.repl.their_item_owner = self.player_names[recipient]
their_item_owner = self.player_names[recipient]
# Write to game display.
await self.repl.write_game_text()
self.repl.queue_game_text(my_item_name, my_item_finder, their_item_name, their_item_owner)
def on_print_json(self, args: dict) -> None:

View File

@@ -1,7 +1,10 @@
import json
import queue
import time
import struct
import random
from dataclasses import dataclass
from queue import Queue
from typing import Dict, Optional
import pymem
@@ -22,6 +25,14 @@ from ..locs import (
OrbCacheLocations as Caches)
@dataclass
class JsonMessageData:
my_item_name: Optional[str] = None
my_item_finder: Optional[str] = None
their_item_name: Optional[str] = None
their_item_owner: Optional[str] = None
class JakAndDaxterReplClient:
ip: str
port: int
@@ -40,11 +51,7 @@ class JakAndDaxterReplClient:
item_inbox: Dict[int, NetworkItem] = {}
inbox_index = 0
my_item_name: Optional[str] = None
my_item_finder: Optional[str] = None
their_item_name: Optional[str] = None
their_item_owner: Optional[str] = None
json_message_queue: Queue[JsonMessageData] = queue.Queue()
def __init__(self, ip: str = "127.0.0.1", port: int = 8181):
self.ip = ip
@@ -81,6 +88,13 @@ class JakAndDaxterReplClient:
await self.receive_deathlink()
self.received_deathlink = False
# Progressively empty the queue during each tick
# if text messages happen to be too slow we could pool dequeuing here,
# but it'd slow down the ItemReceived message during release
if not self.json_message_queue.empty():
json_txt_data = self.json_message_queue.get_nowait()
await self.write_game_text(json_txt_data)
# This helper function formats and sends `form` as a command to the REPL.
# ALL commands to the REPL should be sent using this function.
async def send_form(self, form: str, print_ok: bool = True) -> bool:
@@ -203,20 +217,25 @@ class JakAndDaxterReplClient:
result = result[:32].upper()
return f"\"{result}\""
# Pushes a JsonMessageData object to the json message queue to be processed during the repl main_tick
def queue_game_text(self, my_item_name, my_item_finder, their_item_name, their_item_owner):
self.json_message_queue.put(self.JsonMessageData(my_item_name, my_item_finder,
their_item_name, their_item_owner))
# OpenGOAL can handle both its own string datatype and C-like character pointers (charp).
# So for the game to constantly display this information in the HUD, we have to write it
# to a memory address as a char*.
async def write_game_text(self):
async def write_game_text(self, data: JsonMessageData):
logger.debug(f"Sending info to in-game display!")
await self.send_form(f"(begin "
f" (charp<-string (-> *ap-info-jak1* my-item-name) "
f" {self.sanitize_game_text(self.my_item_name)}) "
f" {self.sanitize_game_text(data.my_item_name)}) "
f" (charp<-string (-> *ap-info-jak1* my-item-finder) "
f" {self.sanitize_game_text(self.my_item_finder)}) "
f" {self.sanitize_game_text(data.my_item_finder)}) "
f" (charp<-string (-> *ap-info-jak1* their-item-name) "
f" {self.sanitize_game_text(self.their_item_name)}) "
f" {self.sanitize_game_text(data.their_item_name)}) "
f" (charp<-string (-> *ap-info-jak1* their-item-owner) "
f" {self.sanitize_game_text(self.their_item_owner)}) "
f" {self.sanitize_game_text(data.their_item_owner)}) "
f" (none))", print_ok=False)
async def receive_item(self):