From d1eda38745274ebf8f6e5881eb92b3ac4e46a3c2 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Wed, 27 Apr 2022 22:11:11 +0200 Subject: [PATCH] Clients: centralize UI and input behaviour --- CommonClient.py | 54 +++++++++++++++++++++++++++++------------------ FF1Client.py | 32 +++++++++++++--------------- FactorioClient.py | 36 ++++++++++++++++--------------- OoTClient.py | 33 +++++++++++++++-------------- SNIClient.py | 36 +++++++++++++++---------------- kvui.py | 37 -------------------------------- 6 files changed, 102 insertions(+), 126 deletions(-) diff --git a/CommonClient.py b/CommonClient.py index 1603c87faf8a..a8374f3d0d98 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -119,9 +119,11 @@ class CommonContext(): starting_reconnect_delay: int = 5 current_reconnect_delay: int = starting_reconnect_delay command_processor: int = ClientCommandProcessor - game = None + game: typing.Optional[str] = None ui = None - keep_alive_task = None + ui_task: typing.Optional[asyncio.Task] = None + input_task: typing.Optional[asyncio.Task] = None + keep_alive_task: typing.Optional[asyncio.Task] = None items_handling: typing.Optional[int] = None current_energy_link_value = 0 # to display in UI, gets set by server @@ -309,6 +311,10 @@ async def shutdown(self): self.input_queue.put_nowait(None) self.input_requests -= 1 self.keep_alive_task.cancel() + if self.ui_task: + await self.ui_task + if self.input_task: + self.input_task.cancel() # DeathLink hooks @@ -343,6 +349,27 @@ async def update_death_link(self, death_link): if old_tags != self.tags and self.server and not self.server.socket.closed: await self.send_msgs([{"cmd": "ConnectUpdate", "tags": self.tags}]) + def run_gui(self): + """Import kivy UI system and start running it as self.ui_task.""" + from kvui import GameManager + + class TextManager(GameManager): + logging_pairs = [ + ("Client", "Archipelago") + ] + base_title = "Archipelago Text Client" + + self.ui = TextManager(self) + self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + + def run_cli(self): + if sys.stdin: + # steam overlay breaks when starting console_loop + if 'gameoverlayrenderer' in os.environ.get('LD_PRELOAD', ''): + logger.info("Skipping terminal input, due to conflicting Steam Overlay detected. Please use GUI only.") + else: + self.input_task = asyncio.create_task(console_loop(self), name="Input") + async def keep_alive(ctx: CommonContext, seconds_between_checks=100): """some ISPs/network configurations drop TCP connections if no payload is sent (ignore TCP-keep-alive) @@ -565,7 +592,6 @@ async def process_server_cmd(ctx: CommonContext, args: dict): async def console_loop(ctx: CommonContext): - import sys commandprocessor = ctx.command_processor(ctx) queue = asyncio.Queue() stream_input(sys.stdin, queue) @@ -620,26 +646,14 @@ def on_package(self, cmd: str, args: dict): async def main(args): ctx = TextContext(args.connect, args.password) ctx.server_task = asyncio.create_task(server_loop(ctx), name="server loop") - input_task = None - steam_overlay = False if gui_enabled: - from kvui import TextManager - ctx.ui = TextManager(ctx) - ui_task = asyncio.create_task(ctx.ui.async_run(), name="UI") - steam_overlay = 'gameoverlayrenderer' in os.environ.get('LD_PRELOAD', '') - else: - ui_task = None - if sys.stdin and not steam_overlay: # steam overlay breaks when starting console_loop - input_task = asyncio.create_task(console_loop(ctx), name="Input") - await ctx.exit_event.wait() + ctx.run_gui() + ctx.run_cli() + await ctx.exit_event.wait() await ctx.shutdown() - if ui_task: - await ui_task - if input_task: - input_task.cancel() import colorama @@ -648,7 +662,5 @@ async def main(args): args, rest = parser.parse_known_args() colorama.init() - loop = asyncio.get_event_loop() - loop.run_until_complete(main(args)) - loop.close() + asyncio.run(main(args)) colorama.deinit() diff --git a/FF1Client.py b/FF1Client.py index c40449682a01..f9f4d00ee09e 100644 --- a/FF1Client.py +++ b/FF1Client.py @@ -102,6 +102,18 @@ def on_package(self, cmd: str, args: dict): f"{receiving_player_name}" self._set_message(msg, item.item) + def run_gui(self): + from kvui import GameManager + + class FF1Manager(GameManager): + logging_pairs = [ + ("Client", "Archipelago") + ] + base_title = "Archipelago Final Fantasy 1 Client" + + self.ui = FF1Manager(self) + self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + def get_payload(ctx: FF1Context): current_time = time.time() @@ -232,14 +244,8 @@ async def main(args): ctx = FF1Context(args.connect, args.password) ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop") if gui_enabled: - input_task = None - from kvui import FF1Manager - ctx.ui = FF1Manager(ctx) - ui_task = asyncio.create_task(ctx.ui.async_run(), name="UI") - else: - input_task = asyncio.create_task(console_loop(ctx), name="Input") - ui_task = None - + ctx.run_gui() + ctx.run_cli() ctx.nes_sync_task = asyncio.create_task(nes_sync_task(ctx), name="NES Sync") await ctx.exit_event.wait() @@ -250,12 +256,6 @@ async def main(args): if ctx.nes_sync_task: await ctx.nes_sync_task - if ui_task: - await ui_task - - if input_task: - input_task.cancel() - import colorama @@ -263,7 +263,5 @@ async def main(args): args, rest = parser.parse_known_args() colorama.init() - loop = asyncio.get_event_loop() - loop.run_until_complete(main(args)) - loop.close() + asyncio.run(main(args)) colorama.deinit() diff --git a/FactorioClient.py b/FactorioClient.py index 0b85dca33115..dd1792ca2a5a 100644 --- a/FactorioClient.py +++ b/FactorioClient.py @@ -125,6 +125,20 @@ def on_package(self, cmd: str, args: dict): f"{Utils.format_SI_prefix(args['value'])}J remaining.") self.rcon_client.send_command(f"/ap-energylink {gained}") + def run_gui(self): + from kvui import GameManager + + class FactorioManager(GameManager): + logging_pairs = [ + ("Client", "Archipelago"), + ("FactorioServer", "Factorio Server Log"), + ("FactorioWatcher", "Bridge Data Log"), + ] + base_title = "Archipelago Factorio Client" + + self.ui = FactorioManager(self) + self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + async def game_watcher(ctx: FactorioContext): bridge_logger = logging.getLogger("FactorioWatcher") @@ -346,15 +360,11 @@ async def factorio_spinup_server(ctx: FactorioContext) -> bool: async def main(args): ctx = FactorioContext(args.connect, args.password) ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop") - input_task = None + if gui_enabled: - from kvui import FactorioManager - ctx.ui = FactorioManager(ctx) - ui_task = asyncio.create_task(ctx.ui.async_run(), name="UI") - else: - ui_task = None - if sys.stdin: - input_task = asyncio.create_task(console_loop(ctx), name="Input") + ctx.run_gui() + ctx.run_cli() + factorio_server_task = asyncio.create_task(factorio_spinup_server(ctx), name="FactorioSpinupServer") successful_launch = await factorio_server_task if successful_launch: @@ -370,12 +380,6 @@ async def main(args): await ctx.shutdown() - if ui_task: - await ui_task - - if input_task: - input_task.cancel() - class FactorioJSONtoTextParser(JSONtoTextParser): def _handle_color(self, node: JSONMessagePart): @@ -416,7 +420,5 @@ def _handle_color(self, node: JSONMessagePart): server_args = ("--rcon-port", rcon_port, "--rcon-password", rcon_password, *rest) - loop = asyncio.get_event_loop() - loop.run_until_complete(main(args)) - loop.close() + asyncio.run(main(args)) colorama.deinit() diff --git a/OoTClient.py b/OoTClient.py index a469f225c3a3..ca758c7bb62e 100644 --- a/OoTClient.py +++ b/OoTClient.py @@ -48,9 +48,11 @@ oot_loc_name_to_id = network_data_package["games"]["Ocarina of Time"]["location_name_to_id"] + def get_item_value(ap_id): return ap_id - 66000 + class OoTCommandProcessor(ClientCommandProcessor): def __init__(self, ctx): super().__init__(ctx) @@ -91,6 +93,18 @@ def on_deathlink(self, data: dict): self.deathlink_pending = True super().on_deathlink(data) + def run_gui(self): + from kvui import GameManager + + class OoTManager(GameManager): + logging_pairs = [ + ("Client", "Archipelago") + ] + base_title = "Archipelago Ocarina of Time Client" + + self.ui = OoTManager(self) + self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + def get_payload(ctx: OoTContext): if ctx.deathlink_enabled and ctx.deathlink_pending: @@ -253,13 +267,8 @@ async def main(): ctx = OoTContext(args.connect, args.password) ctx.server_task = asyncio.create_task(server_loop(ctx), name="Server Loop") if gui_enabled: - input_task = None - from kvui import OoTManager - ctx.ui = OoTManager(ctx) - ui_task = asyncio.create_task(ctx.ui.async_run(), name="UI") - else: - input_task = asyncio.create_task(console_loop(ctx), name="Input") - ui_task = None + ctx.run_gui() + ctx.run_cli() ctx.n64_sync_task = asyncio.create_task(n64_sync_task(ctx), name="N64 Sync") @@ -271,17 +280,9 @@ async def main(): if ctx.n64_sync_task: await ctx.n64_sync_task - if ui_task: - await ui_task - - if input_task: - input_task.cancel() - import colorama colorama.init() - loop = asyncio.get_event_loop() - loop.run_until_complete(main()) - loop.close() + asyncio.run(main()) colorama.deinit() diff --git a/SNIClient.py b/SNIClient.py index 9ba9871762b8..1fa46899fff4 100644 --- a/SNIClient.py +++ b/SNIClient.py @@ -186,6 +186,19 @@ def on_package(self, cmd: str, args: dict): # Once the games handled by SNIClient gets made to be remote items, this will no longer be needed. asyncio.create_task(self.send_msgs([{"cmd": "LocationScouts", "locations": list(new_locations)}])) + def run_gui(self): + from kvui import GameManager + + class SNIManager(GameManager): + logging_pairs = [ + ("Client", "Archipelago"), + ("SNES", "SNES"), + ] + base_title = "Archipelago SNI Client" + + self.ui = SNIManager(self) + self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + async def deathlink_kill_player(ctx: Context): ctx.death_state = DeathState.killing_player @@ -1291,15 +1304,10 @@ async def main(): ctx = Context(args.snes, args.connect, args.password) if ctx.server_task is None: ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop") - input_task = None + if gui_enabled: - from kvui import SNIManager - ctx.ui = SNIManager(ctx) - ui_task = asyncio.create_task(ctx.ui.async_run(), name="UI") - else: - ui_task = None - if sys.stdin: - input_task = asyncio.create_task(console_loop(ctx), name="Input") + ctx.run_gui() + ctx.run_cli() snes_connect_task = asyncio.create_task(snes_connect(ctx, ctx.snes_address), name="SNES Connect") watcher_task = asyncio.create_task(game_watcher(ctx), name="GameWatcher") @@ -1315,12 +1323,6 @@ async def main(): await watcher_task await ctx.shutdown() - if ui_task: - await ui_task - - if input_task: - input_task.cancel() - def get_alttp_settings(romfile: str): lastSettings = Utils.get_adjuster_settings(GAME_ALTTP) @@ -1432,7 +1434,7 @@ def onButtonClick(answer: str = 'no'): if hasattr(lastSettings, "world"): delattr(lastSettings, "world") else: - adjusted = False; + adjusted = False if adjusted: try: shutil.move(adjustedromfile, romfile) @@ -1446,7 +1448,5 @@ def onButtonClick(answer: str = 'no'): if __name__ == '__main__': colorama.init() - loop = asyncio.get_event_loop() - loop.run_until_complete(main()) - loop.close() + asyncio.run(main()) colorama.deinit() diff --git a/kvui.py b/kvui.py index 9e619305dcc9..c11301031d00 100644 --- a/kvui.py +++ b/kvui.py @@ -369,15 +369,6 @@ def set_new_energy_link_value(self): self.energy_link_label.text = f"EL: {Utils.format_SI_prefix(self.ctx.current_energy_link_value)}J" -class FactorioManager(GameManager): - logging_pairs = [ - ("Client", "Archipelago"), - ("FactorioServer", "Factorio Server Log"), - ("FactorioWatcher", "Bridge Data Log"), - ] - base_title = "Archipelago Factorio Client" - - class ChecksFinderManager(GameManager): logging_pairs = [ ("Client", "Archipelago") @@ -385,34 +376,6 @@ class ChecksFinderManager(GameManager): base_title = "Archipelago ChecksFinder Client" -class SNIManager(GameManager): - logging_pairs = [ - ("Client", "Archipelago"), - ("SNES", "SNES"), - ] - base_title = "Archipelago SNI Client" - - -class TextManager(GameManager): - logging_pairs = [ - ("Client", "Archipelago") - ] - base_title = "Archipelago Text Client" - - -class FF1Manager(GameManager): - logging_pairs = [ - ("Client", "Archipelago") - ] - base_title = "Archipelago Final Fantasy 1 Client" - -class OoTManager(GameManager): - logging_pairs = [ - ("Client", "Archipelago") - ] - base_title = "Archipelago Ocarina of Time Client" - - class LogtoUI(logging.Handler): def __init__(self, on_log): super(LogtoUI, self).__init__(logging.INFO)