Launcher: display window for failed world loads if any (#6058)

This commit is contained in:
Silvris
2026-05-05 00:30:47 -05:00
committed by GitHub
parent 4486510fbc
commit 6d9d340c71
5 changed files with 52 additions and 6 deletions
+2 -1
View File
@@ -585,7 +585,8 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
raise Exception(f"Invalid game: {ret.game}")
if ret.game not in AutoWorldRegister.world_types:
from worlds import failed_world_loads
picks = Utils.get_fuzzy_results(ret.game, list(AutoWorldRegister.world_types) + failed_world_loads, limit=1)[0]
picks = Utils.get_fuzzy_results(ret.game, list(AutoWorldRegister.world_types) + list(failed_world_loads.keys()),
limit=1)[0]
if picks[0] in failed_world_loads:
raise Exception(f"No functional world found to handle game {ret.game}. "
f"Did you mean '{picks[0]}' ({picks[1]}% sure)? "
+35
View File
@@ -36,6 +36,7 @@ if __name__ == "__main__":
init_logging('Launcher')
from worlds.LauncherComponents import Component, components, icon_paths, SuffixIdentifier, Type
from worlds import failed_world_loads
def open_host_yaml():
@@ -275,6 +276,7 @@ def run_gui(launch_components: list[Component], args: Any) -> None:
search_box: MDTextField = ObjectProperty(None)
cards: list[LauncherCard]
current_filter: Sequence[str | Type] | None
failed_worlds: bool = bool(failed_world_loads)
def __init__(self, ctx=None, components=None, args=None):
self.title = self.base_title + " " + Utils.__version__
@@ -422,6 +424,39 @@ def run_gui(launch_components: list[Component], args: Any) -> None:
MDSnackbar(MDSnackbarText(text=open_text), y=dp(24), pos_hint={"center_x": 0.5},
size_hint_x=0.5).open()
@staticmethod
def copy_to_clipboard(text):
from kivy.core.clipboard import Clipboard
Clipboard.copy(text)
MDSnackbar(MDSnackbarText(text="Copied to clipboard."), y=dp(24), pos_hint={"center_x": 0.5},
size_hint_x=0.5).open()
def display_failed(self):
"""Display a dialog showing the exceptions produced by any world that failed to load during
initialization."""
if not self.failed_worlds:
return
from kivymd.uix.dialog import MDDialog, MDDialogIcon, MDDialogHeadlineText, MDDialogContentContainer
from kivymd.uix.divider import MDDivider
from kivymd.uix.list import MDListItem, MDListItemHeadlineText, MDListItemSupportingText
entries = []
for world, reason in failed_world_loads.items():
entries.append(MDListItem(
MDListItemHeadlineText(text=world),
MDListItemSupportingText(text=reason),
on_release=lambda x, r=reason: self.copy_to_clipboard(r)
))
dialog = MDDialog(
MDDialogIcon(icon="alert"),
MDDialogHeadlineText(text="Failed World Loads"),
MDDialogContentContainer(
MDDivider(),
*entries,
orientation="vertical",
)
)
dialog.open()
def _on_drop_file(self, window: Window, filename: bytes, x: int, y: int) -> None:
""" When a patch file is dropped into the window, run the associated component. """
file, component = identify(filename.decode())
+9
View File
@@ -140,6 +140,15 @@ MDFloatLayout:
MDNavigationDrawerDivider:
MDBoxLayout:
orientation: "horizontal"
MDIconButton:
icon: "alert" if app.failed_worlds else ""
theme_text_color: "Custom"
text_color: "D23C42"
disabled: not app.failed_worlds
on_release: app.display_failed()
MDGridLayout:
id: main_layout
+1 -1
View File
@@ -54,7 +54,7 @@ class TestImplemented(unittest.TestCase):
def test_no_failed_world_loads(self):
if failed_world_loads:
self.fail(f"The following worlds failed to load: {failed_world_loads}")
self.fail(f"The following worlds failed to load: {failed_world_loads.keys()}")
def test_prefill_items(self):
"""Test that every world can reach every location from allstate before pre_fill."""
+5 -4
View File
@@ -33,7 +33,7 @@ __all__ = [
]
failed_world_loads: List[str] = []
failed_world_loads: dict[str, str] = {}
@dataclasses.dataclass(order=True)
@@ -68,8 +68,9 @@ class WorldSource:
print(f"Could not load world {self}:", file=file_like)
traceback.print_exc(file=file_like)
file_like.seek(0)
logging.exception(file_like.read())
failed_world_loads.append(os.path.basename(self.path).rsplit(".", 1)[0])
reason = file_like.read()
logging.exception(reason)
failed_world_loads[os.path.basename(self.path).rsplit(".", 1)[0]] = reason
return False
@@ -128,7 +129,7 @@ if apworlds:
def fail_world(game_name: str, reason: str, add_as_failed_to_load: bool = True) -> None:
if add_as_failed_to_load:
failed_world_loads.append(game_name)
failed_world_loads[game_name] = reason
logging.warning(reason)
for apworld_source in apworlds: