diff --git a/CommonClient.py b/CommonClient.py index bd8e1248022d..c4d68faed420 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -127,6 +127,7 @@ class CommonContext(): items_handling: typing.Optional[int] = None slot_info: typing.Dict[int, NetworkSlot] current_energy_link_value: int = 0 # to display in UI, gets set by server + _messagebox = None def __init__(self, server_address, password): # server state @@ -356,6 +357,27 @@ async def update_death_link(self, death_link: bool): if old_tags != self.tags and self.server and not self.server.socket.closed: await self.send_msgs([{"cmd": "ConnectUpdate", "tags": self.tags}]) + def gui_error(self, title: str, text: typing.Union[Exception, str]): + """Displays an error messagebox""" + if not self.ui: + return + title = title or "Error" + from kvui import MessageBox + if self._messagebox: + self._messagebox.dismiss() + # make "Multiple exceptions" look nice + text = str(text).replace('[Errno', '\n[Errno').strip() + # split long messages into title and text + parts = title.split('. ', 1) + if len(parts) == 1: + parts = title.split(', ', 1) + if len(parts) > 1: + text = parts[1] + '\n\n' + text + title = parts[0] + # display error + self._messagebox = MessageBox(title, text, error=True) + self._messagebox.open() + def run_gui(self): """Import kivy UI system and start running it as self.ui_task.""" from kvui import GameManager @@ -418,14 +440,22 @@ async def server_loop(ctx: CommonContext, address=None): for msg in decode(data): await process_server_cmd(ctx, msg) logger.warning('Disconnected from multiworld server, type /connect to reconnect') - except ConnectionRefusedError: - logger.exception('Connection refused by the server. May not be running Archipelago on that address or port.') - except websockets.InvalidURI: - logger.exception('Failed to connect to the multiworld server (invalid URI)') - except OSError: - logger.exception('Failed to connect to the multiworld server') - except Exception: - logger.exception('Lost connection to the multiworld server, type /connect to reconnect') + except ConnectionRefusedError as e: + msg = 'Connection refused by the server. May not be running Archipelago on that address or port.' + logger.exception(msg, extra={'compact_gui': True}) + ctx.gui_error(msg, e) + except websockets.InvalidURI as e: + msg = 'Failed to connect to the multiworld server (invalid URI)' + logger.exception(msg, extra={'compact_gui': True}) + ctx.gui_error(msg, e) + except OSError as e: + msg = 'Failed to connect to the multiworld server' + logger.exception(msg, extra={'compact_gui': True}) + ctx.gui_error(msg, e) + except Exception as e: + msg = 'Lost connection to the multiworld server, type /connect to reconnect' + logger.exception(msg, extra={'compact_gui': True}) + ctx.gui_error(msg, e) finally: await ctx.connection_closed() if ctx.server_address: @@ -448,7 +478,9 @@ async def process_server_cmd(ctx: CommonContext, args: dict): raise if cmd == 'RoomInfo': if ctx.seed_name and ctx.seed_name != args["seed_name"]: - logger.info("The server is running a different multiworld than your client is. (invalid seed_name)") + msg = "The server is running a different multiworld than your client is. (invalid seed_name)" + logger.info(msg, extra={'compact_gui': True}) + ctx.gui_error('Error', msg) else: logger.info('--------------------------------') logger.info('Room Information:') diff --git a/FactorioClient.py b/FactorioClient.py index 31798392dde9..b7bf324311ad 100644 --- a/FactorioClient.py +++ b/FactorioClient.py @@ -150,7 +150,9 @@ async def game_watcher(ctx: FactorioContext): next_bridge = time.perf_counter() + 1 ctx.awaiting_bridge = False data = json.loads(ctx.rcon_client.send_command("/ap-sync")) - if data["slot_name"] != ctx.auth: + if not ctx.auth: + pass # auth failed, wait for new attempt + elif data["slot_name"] != ctx.auth: bridge_logger.warning(f"Connected World is not the expected one {data['slot_name']} != {ctx.auth}") elif data["seed_name"] != ctx.seed_name: bridge_logger.warning( @@ -342,8 +344,10 @@ async def factorio_spinup_server(ctx: FactorioContext) -> bool: await asyncio.sleep(0.01) except Exception as e: - logger.exception(e) - logger.error("Aborted Factorio Server Bridge") + logger.exception(e, extra={"compact_gui": True}) + msg = "Aborted Factorio Server Bridge" + logger.error(msg) + ctx.gui_error(msg, e) ctx.exit_event.set() else: diff --git a/kvui.py b/kvui.py index 14eb09194a1e..7afd43636b7d 100644 --- a/kvui.py +++ b/kvui.py @@ -464,8 +464,19 @@ def __init__(self, on_log): super(LogtoUI, self).__init__(logging.INFO) self.on_log = on_log + @staticmethod + def format_compact(record: logging.LogRecord) -> str: + if isinstance(record.msg, Exception): + return str(record.msg) + return (f'{record.exc_info[1]}\n' if record.exc_info else '') + str(record.msg).split("\n")[0] + def handle(self, record: logging.LogRecord) -> None: - self.on_log(self.format(record)) + if getattr(record, 'skip_gui', False): + pass # skip output + elif getattr(record, 'compact_gui', False): + self.on_log(self.format_compact(record)) + else: + self.on_log(self.format(record)) class UILog(RecycleView):