Merge pull request #366 from aclist/fix/validate-offline-mods

fix: validate custom folders before launch
This commit is contained in:
aclist 2026-06-17 22:13:56 +09:00 committed by GitHub
commit 1dd773a9bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 38 additions and 1 deletions

View File

@ -34,10 +34,18 @@ class OfflineManager:
self.thread_man = ThreadingManager(controller)
self.appid: int
self.mission_folder: str
self.local_mods: list[str] | None
self.custom_mods: list[str] | None
# TODO: more robust test
def is_custom_folder_valid(self, folder: str) -> bool:
return Path(folder).exists()
# TODO: set properties as members of offline manager on demand and check them here
# rather than passing strings again
def is_mission_valid(self, folder: str) -> bool:
return is_mission(Path(folder))
def get_mission(self) -> None:
folder = self.open_folderpicker(dialogs.mission_dialog)
if folder is None:

View File

@ -13,6 +13,7 @@ local_frame = "Installed mods"
no_local_mods = "No local mods found"
no_mods = "No valid mods found"
no_mission = "Not a valid mission"
folder_changed = "Folder missing or changed on disk"
custom_eventbox = "Set the root folder. Mods must be in atomic folders and have a meta.cpp file at a minimum."
mission_eventbox = "Select a single mission folder containing an init.c file."

View File

@ -38,6 +38,7 @@ if TYPE_CHECKING:
class FolderError(Enum):
NO_VALID_MODS = 1
NO_VALID_MISSION = 2
FOLDER_CHANGED = 3
class PageHeading(Gtk.Label):
@ -65,6 +66,10 @@ class ErrorPopover(Gtk.Popover):
prefix = offline.no_mods
case FolderError.NO_VALID_MISSION:
prefix = offline.no_mission
case FolderError.FOLDER_CHANGED:
prefix = offline.folder_changed
self.label.set_label(prefix)
return
self.label.set_label(f"{prefix}: '{msg}'")
@ -105,6 +110,11 @@ class FolderHBox(HBox):
self.pop.set_relative_to(self.button)
self.pop.connect("unmap", lambda _: self.grab_focus())
self.sidepop = ErrorPopover()
self.sidepop.set_position(Gtk.PositionType.BOTTOM)
self.sidepop.set_relative_to(self.scrolled_label)
self.sidepop.connect("unmap", lambda _: self.grab_focus())
self.connect("map", self._on_map)
self.connect("unmap", self._on_unmap)
@ -150,6 +160,10 @@ class FolderHBox(HBox):
self.unset_button.show()
def present_error(self, error: FolderError, msg: str) -> None:
if error == FolderError.FOLDER_CHANGED:
self.sidepop.set_label(error, msg)
self.sidepop.popup()
return
self.folder = ""
self.label.set_text("")
self.unset_button.hide()
@ -300,6 +314,9 @@ class CustomModFrame(ModFrame):
self.tree_vbox.hide()
self.no_mods.hide()
def present_folder_changed(self, folder: str) -> None:
self.custom_hbox.present_error(FolderError.FOLDER_CHANGED, folder)
def present_error(self, folder: str) -> None:
self.hide_tree()
self.custom_hbox.present_error(FolderError.NO_VALID_MODS, folder)
@ -363,6 +380,9 @@ class MissionFrame(HeadingFrame):
def get_mission(self) -> str:
return self.mission_hbox.get_folder()
def present_folder_changed(self, folder: str) -> None:
self.mission_hbox.present_error(FolderError.FOLDER_CHANGED, folder)
class RadioFrame(HeadingFrame):
def __init__(self, parent: OfflineLoader, controller: "Controller") -> None:
@ -482,4 +502,12 @@ class OfflineLoader(Gtk.Box):
local_mods = self.local_frame.get_mods()
custom_folder = self.custom_frame.get_folder()
custom_mods = self.custom_frame.get_mods()
if custom_folder and not self.offline_man.is_custom_folder_valid(custom_folder):
self.custom_frame.present_folder_changed(mission)
return
if mission and not self.offline_man.is_mission_valid(mission):
self.mission_frame.present_folder_changed(mission)
return
self.offline_man.launch(appid, mission, local_mods, custom_folder, custom_mods)