Commit Graph
6677 Commits
Author SHA1 Message Date
Inutt 523f890e7b Fix if->elif left over from reverts 2025-12-05 00:25:44 +00:00
Inutt 140f0fbc41 Revert "[#2532] Ignore Loadout If Embarked"
This reverts commit b85705c13a.
2025-12-05 00:23:15 +00:00
Inutt 26e3407f53 Revert "Add Multicrew Check Too"
This reverts commit a8e34f2a3b.
2025-12-05 00:20:14 +00:00
Inutt 733a518341 Set player's multicrew role to idle when physically boarding another player's ship 2025-12-04 23:56:07 +00:00
David Sangrey 0e28cd4962 [Minor] Remove Unneeded Keys Calls 2025-12-01 22:08:16 -05:00
David Sangrey 2041b3d3d3 [Minor] Update Type Hints and Alias Usages 2025-12-01 22:04:28 -05:00
David Sangrey b63b4b90ca [Minor] Move Linter Warning 2025-12-01 21:54:14 -05:00
David Sangrey a9693dca4c Merge pull request #2534 from inutt/fix/2345/relative-imports
Fix/2345/relative imports
2025-12-01 21:51:36 -05:00
David Sangrey 1beddea8e0 Merge pull request #2538 from inutt/fix/--/linting
Fix mypy linting issues
2025-12-01 21:51:20 -05:00
David Sangrey 88c21ec883 Merge branch 'develop' into fix/2345/relative-imports 2025-12-01 21:49:52 -05:00
David Sangrey 6d9b200ea8 [#2535] Redo CSV Exporter 2025-12-01 21:46:47 -05:00
David Sangrey 04bbedff29 Merge pull request #2548 from EDCD/dependabot/pip/develop/mypy-1.19.0
Bump mypy from 1.18.2 to 1.19.0
2025-12-01 20:57:27 -05:00
David Sangrey 92601d7b7f Merge pull request #2540 from inutt/enhancement/2404/localised-system-colonisation-ship-name
Add localisation of stations names
2025-12-01 20:46:02 -05:00
David Sangrey 98d37d7676 Merge pull request #2544 from EDCD/dependabot/github_actions/develop/actions/checkout-6
Bump actions/checkout from 5 to 6
2025-12-01 20:45:09 -05:00
David Sangrey fc139c0c5d Merge pull request #2546 from EDCD/dependabot/pip/develop/pytest-9.0.1
Bump pytest from 9.0.0 to 9.0.1
2025-12-01 20:33:38 -05:00
David Sangrey aa7beceef8 Merge pull request #2545 from EDCD/dependabot/pip/develop/pre-commit-4.5.0
Bump pre-commit from 4.4.0 to 4.5.0
2025-12-01 20:33:29 -05:00
David Sangrey 9d374ab612 Merge pull request #2547 from EDCD/dependabot/pip/develop/coverage-toml--7.12.0
Bump coverage[toml] from 7.11.2 to 7.12.0
2025-12-01 20:33:20 -05:00
dependabot[bot] 834158a3c7 Bump mypy from 1.18.2 to 1.19.0
Bumps [mypy](https://github.com/python/mypy) from 1.18.2 to 1.19.0.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.18.2...v1.19.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-version: 1.19.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 19:12:41 +00:00
dependabot[bot] 252e16cade Bump coverage[toml] from 7.11.2 to 7.12.0
Bumps [coverage[toml]](https://github.com/coveragepy/coveragepy) from 7.11.2 to 7.12.0.
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.11.2...7.12.0)

---
updated-dependencies:
- dependency-name: coverage[toml]
  dependency-version: 7.12.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 19:12:36 +00:00
dependabot[bot] 77eee9e8be Bump pytest from 9.0.0 to 9.0.1
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.0 to 9.0.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/9.0.0...9.0.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 19:12:30 +00:00
dependabot[bot] 98f1aed3f0 Bump pre-commit from 4.4.0 to 4.5.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v4.4.0...v4.5.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-version: 4.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 19:12:23 +00:00
dependabot[bot] 4fc73c39cd Bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 18:49:07 +00:00
David Sangrey 28cc39fd1d Merge pull request #2542 from inutt/fix/2541/preferences-path-display
Fix preference dialog not detecting tab change events
2025-11-30 21:10:33 -05:00
Inutt c5961cd91c Fix preference dialog not detecting tab change events 2025-11-29 03:26:21 +00:00
Inutt 9fd04e5c02 Add localisation of stations names
This currently only affects system colonisation ships, which have '$EXT_PANEL_ColonisationShip;' in their StationName
entries in the journal. Other strings may need to be added in future.

Fixes #2404
diff --git a/L10n/en.template b/L10n/en.template
index faac3259..f670e39c 100644
--- a/L10n/en.template
+++ b/L10n/en.template
@@ -839,3 +839,9 @@

 /* edastro_core.py: Settings>EDAstro - Label on checkbox for 'send data'; */
 "Send data to EDAstro" = "Send data to EDAstro";
+
+/* common_coreutils.py: Colonisation ship name, with title; In files: common_coreutils.py:81:81 */
+"System Colonisation Ship {NAME}" = "System Colonisation Ship {NAME}";
+
+/* common_coreutils.py: Unidentified colonisation ship; In files: common_coreutils.py:84:84 */
+"System Colonisation Ship" = "System Colonisation Ship";
diff --git a/plugins/common_coreutils.py b/plugins/common_coreutils.py
index 6b90d4dc..a488de46 100644
--- a/plugins/common_coreutils.py
+++ b/plugins/common_coreutils.py
@@ -29,6 +29,7 @@ import gzip
 import io
 import json
 import os
+import re
 import myNotebook as nb  # noqa: N813
 from EDMCLogging import get_main_logger
 from companion import CAPIData
@@ -47,6 +48,49 @@ SEPY = 10  # seperator line spacing
 STATION_UNDOCKED = '×'  # "Station" name to display when not docked = U+00D7

+# Known colonisation ship names match one of the following:
+#   "$EXT_PANEL_ColonisationShip;"
+#   "$EXT_PANEL_ColonisationShip; <ship name>" with "<ship name>" being eg; "Okawara Horizons"
+#   "$EXT_PANEL_ColonisationShip:#index=<number>;"
+#   "$EXT_PANEL_ColonisationShip:#index=<number>; <ship name>"
+_RE_COLONISATION_SHIP = re.compile(r'\$EXT_PANEL_ColonisationShip(?::#index=(\d+))?;(?: (.+))?')
+
+
+def localised_name(value: str | None) -> str | None:
+    """
+    Handle localisation of strings in places like station names.
+
+    This is currently an issue for system colonisation ships that have a station name along the
+    lines of "$EXT_PANEL_ColonisationShip; Okawara Horizons" when written to the journal, but would
+    be better displayed as "System Colonisation Ship Okawara Horizona" (or other appropriately
+    localised equivalent).
+
+    If no (known) localisable strings are present, the input will be returned unmodified.
+
+    :param value: The non-localised string.
+    :return: The localised string, or None if the input was None.
+    """
+    if value is None:
+        return None
+
+    colonisation_ship_match = _RE_COLONISATION_SHIP.match(value)
+    if colonisation_ship_match:
+        ship_index = colonisation_ship_match.group(1)
+        ship_name = colonisation_ship_match.group(2)
+        # Use the ship's index as its name if no name was provided.
+        ship_id = ship_name or ship_index
+
+        if ship_id:
+            # LANG: Colonisation ship name, with title
+            value = tr.tl("System Colonisation Ship {NAME}").format(NAME=ship_id)
+
+        else:
+            # LANG: Unidentified colonisation ship
+            value = tr.tl("System Colonisation Ship")
+
+    return value
+
+
 def plugin_start3(plugin_dir: str) -> str:
     """
     Start the plugin.
@@ -121,7 +165,7 @@ def station_link_common(data: CAPIData, this):
     :param this: The module global from the calling module.
     """
     if data['commander']['docked'] or this.on_foot and this.station_name:
-        this.station_link['text'] = this.station_name
+        this.station_link['text'] = localised_name(this.station_name)
     elif data['lastStarport']['name'] and data['lastStarport']['name'] != "":
         this.station_link['text'] = STATION_UNDOCKED
     else:
@@ -163,12 +207,14 @@ def station_name_setter_common(this):
     :param this: The module global from the calling module.
     """
     to_set: str = cast(str, this.station_name)
+
     if not to_set:
         if this.system_population is not None and this.system_population > 0:
             to_set = STATION_UNDOCKED
         else:
             to_set = ''

+    to_set = str(localised_name(to_set))
     this.station_link['text'] = to_set

diff --git a/plugins/edsm.py b/plugins/edsm.py
index e1e6b168..902e248b 100644
--- a/plugins/edsm.py
+++ b/plugins/edsm.py
@@ -43,7 +43,7 @@ from ttkHyperlinkLabel import HyperlinkLabel
 from l10n import translations as tr
 from plugins.common_coreutils import (api_keys_label_common, PADX, PADY, BUTTONX, SEPY, BOXY, STATION_UNDOCKED,
                                       show_pwd_var_common, station_link_common, this_format_common,
-                                      cmdr_data_initial_common)
+                                      cmdr_data_initial_common, localised_name)

 # TODO:
@@ -543,7 +543,7 @@ entry: {entry!r}'''
                 to_set = ''

         if this.station_link:
-            this.station_link['text'] = to_set
+            this.station_link['text'] = localised_name(to_set)
             this.station_link['url'] = station_url(str(this.system_name), str(this.station_name))
             this.station_link.update_idletasks()
2025-11-29 01:25:19 +00:00
Inutt e413172456 Unset monitor.mode on Commander journal event to avoid reusing previous mode
Changing game mode would otherwise re-use the previously used game mode for events before the new LoadGame event. This
resets the mode on the first available event so they are buffered again until the new game mode is determined.
2025-11-28 17:03:09 +00:00
Inutt bf5824223d Fix mypy linting issues
Mostly related to adjusting `if sys.platform == 'win32'` logic so things that are only defined on win32 platforms don't
cause issues on other platforms.
2025-11-28 04:32:52 +00:00
Inutt 42bc097e92 Pass events prior to initial LoadGame event to plugins
Done by temporarily buffering events before monitor.mode is set, then notifying plugins about them before sending the
first event where monitor.mode has a value. LoadGame is currently the only event that sets monitor.mode.

Fixes #2286
diff --git a/EDMarketConnector.py b/EDMarketConnector.py
index e7bdf490..d53b625d 100644
--- a/EDMarketConnector.py
+++ b/EDMarketConnector.py
@@ -22,7 +22,7 @@ import threading
 import webbrowser
 from os import chdir, environ
 from time import localtime, strftime, time
-from typing import TYPE_CHECKING, Any, Literal
+from typing import TYPE_CHECKING, Any, Literal, MutableMapping
 from constants import applongname, appname, protocolhandler_redirect

 # Have this as early as possible for people running EDMarketConnector.exe
@@ -458,6 +458,8 @@ class AppWindow:

     def __init__(self, master: tk.Tk):  # noqa: C901, CCR001 # TODO - can possibly factor something out

+        self.early_journal_events: list[MutableMapping[str, Any]] = []
+
         self.capi_query_holdoff_time = config.get_int('querytime', default=0) + companion.capi_query_cooldown
         self.capi_fleetcarrier_query_holdoff_time = config.get_int('fleetcarrierquerytime', default=0) \
             + companion.capi_fleetcarrier_query_cooldown
@@ -1400,6 +1402,19 @@ class AppWindow:
         self.cooldown()
         logger.trace_if('capi.worker', '...done')

+    def handle_plugin_event_error(self, err: str | None) -> None:
+        """
+        Display a plugin's error message at the bottom of the main window.
+
+        If there was no error message passed in, nothing will be done.
+
+        :param err: Message to display, or None for no error.
+        """
+        if err:
+            self.status['text'] = err
+            if not config.get_int('hotkey_mute'):
+                hotkeymgr.play_bad()
+
     def journal_event(self, event: str):  # noqa: C901, CCR001 # Currently not easily broken up.
         """
         Handle a Journal event passed through event queue from monitor.py.
@@ -1513,15 +1528,21 @@ class AppWindow:

             if monitor.cmdr and monitor.mode == 'CQC' and entry['event']:
                 err = plug.notify_journal_entry_cqc(monitor.cmdr, monitor.is_beta, entry, monitor.state)
-                if err:
-                    self.status['text'] = err
-                    if not config.get_int('hotkey_mute'):
-                        hotkeymgr.play_bad()
+                self.handle_plugin_event_error(err)

                 return  # in CQC

             if not entry['event'] or not monitor.mode:
+                # Game session has not fully loaded yet. We may be receiving events from the
+                # journal, but don't know which game mode (Open/CQC/solo etc) we're running in.
                 logger.trace_if('journal.queue', 'Startup, returning')
+
+                if entry['event']:
+                    # Since we don't know which mode the game is in (and therefore whether to send
+                    # the event to a plugin's `journal_event` or `journal_event_cqc` function),
+                    # temporarily queue it so we can pass it on once the mode is known.
+                    self.early_journal_events.append(entry)
+
                 return  # Startup

             if entry['event'] in ('StartUp', 'LoadGame') and monitor.started:
@@ -1538,6 +1559,25 @@ class AppWindow:
                 if not dashboard.start(self.w, monitor.started):
                     logger.info("Can't start Status monitoring")

+            # monitor.cmdr should always be set if monitor.mode is set (and monitor.mode must be set
+            # if we reach this point in the function), but we check monitor.cmdr here so the mypy
+            # doesn't complain about passing str|None where str is required.
+            if self.early_journal_events and monitor.cmdr:
+                for early_entry in self.early_journal_events:
+                    if monitor.mode == 'CQC':
+                        err = plug.notify_journal_entry_cqc(monitor.cmdr, monitor.is_beta, early_entry, monitor.state)
+                    else:
+                        err = plug.notify_journal_entry(
+                            monitor.cmdr,
+                            monitor.is_beta,
+                            monitor.state['SystemName'],
+                            monitor.state['StationName'],
+                            early_entry,
+                            monitor.state
+                        )
+                    self.handle_plugin_event_error(err)
+                self.early_journal_events.clear()
+
             # Export loadout
             if entry['event'] == 'Loadout' and not monitor.state['Captain'] \
                     and config.get_int('output') & config.OUT_SHIP:
@@ -1553,10 +1593,7 @@ class AppWindow:
                     monitor.state
                 )

-                if err:
-                    self.status['text'] = err
-                    if not config.get_int('hotkey_mute'):
-                        hotkeymgr.play_bad()
+                self.handle_plugin_event_error(err)

             auto_update = False
             # Only if auth callback is not pending
@@ -1638,11 +1675,7 @@ class AppWindow:
         # Currently we don't do anything with these events
         if monitor.cmdr:
             err = plug.notify_dashboard_entry(monitor.cmdr, monitor.is_beta, entry)
-
-            if err:
-                self.status['text'] = err
-                if not config.get_int('hotkey_mute'):
-                    hotkeymgr.play_bad()
+            self.handle_plugin_event_error(err)

     def plugin_error(self, event=None) -> None:
         """Display asynchronous error from plugin."""
2025-11-28 03:37:14 +00:00
David Muckle a667882189 Add special case in setting for lists to avoid storing as strings
The `get_list` does a type check if the value returned is a list, and returns default otherwise. Since we were storing everything as a string, if something set a list after the initial migration to toml, that key would then always be returned as the default value
2025-11-25 19:19:32 -05:00
David Muckle d4ac2fe8b1 Cast sets to strings, tweak get_str behavior per PR feedback 2025-11-25 18:42:15 -05:00
Inutt f4e70dbe5b Add documentation for relative plugin imports
diff --git a/PLUGINS.md b/PLUGINS.md
index 6fd20ef9..71aaec7a 100644
--- a/PLUGINS.md
+++ b/PLUGINS.md
@@ -163,6 +163,19 @@ circular imports.
 For an example of how this is done, look at the code in `plugins/common_coreutils.py`
 and the usage of these functions in other core plugins.

+Plugins may import additional code modules from `load.py` in the usual way. Note
+that using an import statement like `import my_module` or `from my_module import
+foo` will only import one module with that name **across all plugins**. This can
+be used to create a shared library used by several plugins if used carefully,
+but may create a conflict if another plugin also has a module of the same name
+containing different code. For example if two plugins both contain a
+`widgets.py` file and both do `import widgets`, only one of the two modules will
+actually be imported.
+
+To avoid this issue, use a relative import statement such as `import .widgets`
+or `from .widgets import foo` to ensure that the module is loaded from the
+current plugin, even if the name conflicts with another plugin's code.
+
 ---

 ## Logging
2025-11-24 20:14:51 +00:00
Inutt b9f3cf3d6a Fix relative imports in plugins
Allow plugins to use relative imports to avoid conflicts between modules with the same name from another plugin.

Fixes #2345
diff --git a/plug.py b/plug.py
index 1a8779f7..8890c80f 100644
--- a/plug.py
+++ b/plug.py
@@ -48,13 +48,20 @@ last_error = LastError()
 class Plugin:
     """An EDMC plugin."""

-    def __init__(self, name: str, loadfile: Path | None, plugin_logger: logging.Logger | None):  # noqa: CCR001
+    def __init__(
+        self,
+        name: str,
+        loadfile: Path | None,
+        plugin_logger: logging.Logger | None,
+        internal: bool = False
+    ):  # noqa: CCR001
         """
         Load a single plugin.

         :param name: Base name of the file being loaded from.
         :param loadfile: Full path/filename of the plugin.
         :param plugin_logger: The logging instance for this plugin to use.
+        :param internal: True to use internal plugin loader logic. Defaults to False.
         :raises Exception: Typically, ImportError or OSError
         """
         self.name: str = name  # Display name.
@@ -65,14 +72,19 @@ class Plugin:
         if loadfile:
             logger.info(f'loading plugin "{name.replace(".", "_")}" from "{loadfile}"')
             try:
-                filename = 'plugin_'
-                filename += name.encode(encoding='ascii', errors='replace').decode('utf-8').replace('.', '_')
-                spec = importlib.util.spec_from_file_location(filename, loadfile)
-                # Replaces older load_module() code. Includes a safety check that the module name is set.
-                if spec is not None and spec.loader is not None:
-                    module = importlib.util.module_from_spec(spec)
-                    sys.modules[module.__name__] = module
-                    spec.loader.exec_module(module)
+                if internal:
+                    filename = 'plugin_'
+                    filename += name.encode(encoding='ascii', errors='replace').decode('utf-8').replace('.', '_')
+                    spec = importlib.util.spec_from_file_location(filename, loadfile)
+                    # Replaces older load_module() code. Includes a safety check that the module name is set.
+                    if spec is not None and spec.loader is not None:
+                        module = importlib.util.module_from_spec(spec)
+                        sys.modules[module.__name__] = module
+                        spec.loader.exec_module(module)
+                else:
+                    module = importlib.import_module('.load', name)
+
+                if module:
                     if getattr(module, 'plugin_start3', None):
                         newname = module.plugin_start3(Path(loadfile).resolve().parent)
                         self.name = str(newname) if newname else self.name
@@ -170,7 +182,7 @@ def _load_internal_plugins():
             try:
                 plugin_name = name[:-3]
                 plugin_path = config.internal_plugin_dir_path / name
-                plugin = Plugin(plugin_name, plugin_path, logger)
+                plugin = Plugin(plugin_name, plugin_path, logger, internal=True)
                 plugin.folder = None
                 internal.append(plugin)
             except Exception:

diff --git a/plug.py b/plug.py
index 1a8779f7..8890c80f 100644
--- a/plug.py
+++ b/plug.py
@@ -48,13 +48,20 @@ last_error = LastError()
 class Plugin:
     """An EDMC plugin."""

-    def __init__(self, name: str, loadfile: Path | None, plugin_logger: logging.Logger | None):  # noqa: CCR001
+    def __init__(
+        self,
+        name: str,
+        loadfile: Path | None,
+        plugin_logger: logging.Logger | None,
+        internal: bool = False
+    ):  # noqa: CCR001
         """
         Load a single plugin.

         :param name: Base name of the file being loaded from.
         :param loadfile: Full path/filename of the plugin.
         :param plugin_logger: The logging instance for this plugin to use.
+        :param internal: True to use internal plugin loader logic. Defaults to False.
         :raises Exception: Typically, ImportError or OSError
         """
         self.name: str = name  # Display name.
@@ -65,14 +72,19 @@ class Plugin:
         if loadfile:
             logger.info(f'loading plugin "{name.replace(".", "_")}" from "{loadfile}"')
             try:
-                filename = 'plugin_'
-                filename += name.encode(encoding='ascii', errors='replace').decode('utf-8').replace('.', '_')
-                spec = importlib.util.spec_from_file_location(filename, loadfile)
-                # Replaces older load_module() code. Includes a safety check that the module name is set.
-                if spec is not None and spec.loader is not None:
-                    module = importlib.util.module_from_spec(spec)
-                    sys.modules[module.__name__] = module
-                    spec.loader.exec_module(module)
+                if internal:
+                    filename = 'plugin_'
+                    filename += name.encode(encoding='ascii', errors='replace').decode('utf-8').replace('.', '_')
+                    spec = importlib.util.spec_from_file_location(filename, loadfile)
+                    # Replaces older load_module() code. Includes a safety check that the module name is set.
+                    if spec is not None and spec.loader is not None:
+                        module = importlib.util.module_from_spec(spec)
+                        sys.modules[module.__name__] = module
+                        spec.loader.exec_module(module)
+                else:
+                    module = importlib.import_module('.load', name)
+
+                if module:
                     if getattr(module, 'plugin_start3', None):
                         newname = module.plugin_start3(Path(loadfile).resolve().parent)
                         self.name = str(newname) if newname else self.name
@@ -170,7 +182,7 @@ def _load_internal_plugins():
             try:
                 plugin_name = name[:-3]
                 plugin_path = config.internal_plugin_dir_path / name
-                plugin = Plugin(plugin_name, plugin_path, logger)
+                plugin = Plugin(plugin_name, plugin_path, logger, internal=True)
                 plugin.folder = None
                 internal.append(plugin)
             except Exception:
2025-11-24 18:00:58 +00:00
David Muckle d5afc4517b Correctly decode lists in Linux .ini file, treat all config keys as lowercase. 2025-11-21 21:02:16 -05:00
David Sangrey 48dafaf31f Update ChangeLog.md 2025-11-20 18:19:04 -05:00
David Sangrey 459a794460 [PRE-RELEASE] 6.0.0-beta2 Release/6.0.0-beta2 2025-11-20 18:03:58 -05:00
David Sangrey a8e34f2a3b Add Multicrew Check Too 2025-11-20 18:00:06 -05:00
David Sangrey b85705c13a [#2532] Ignore Loadout If Embarked 2025-11-20 17:53:57 -05:00
David Sangrey 691e0a66ad Merge pull request #2531 from dvdmuckle/develop
Improve Linux config import
2025-11-19 22:19:11 -05:00
David Muckle f2d2354fb5 Improve linux config import and logging 2025-11-19 21:50:34 -05:00
David Sangrey 8dbd5cae69 [Minor] Add Custom Config Path, Add Additional Logging 2025-11-19 20:57:41 -05:00
David Sangrey 3fee941bbb [Minor] Fix Some Output Tooling 2025-11-18 22:17:26 -05:00
David Sangrey bc09786b28 [RELEASE] 6.0.0-beta1 Release/6.0.0-beta1 2025-11-18 21:50:56 -05:00
David Sangrey 61e8ff0c7a [Minor] Fix "Reset Provider" popup on first run 2025-11-18 18:56:37 -05:00
David Sangrey 4bd6ec6d7a Merge pull request #2528 from HullSeals/enhancement/261/disable-plugins
Scrollable Notebook and Plugin Enable/Disable System
2025-11-18 18:39:23 -05:00
David Sangrey d545a78b1f Merge branch 'beta' into enhancement/261/disable-plugins 2025-11-17 20:55:49 -05:00
David Sangrey 68bc78f30d Merge branch 'main' into stable 2025-11-17 20:42:28 -05:00
David Sangrey 1362177577 Merge branch 'beta' 2025-11-17 20:41:31 -05:00
David Sangrey 7a56ea78dc [5.13.3] Update Changelog Release/5.13.3 2025-11-17 19:42:48 -05:00
Ben Peddell f80ac51fe2 [Bugfix] Fix race starting journal monitor
Fixes a race identified where long plugin init times can result in
the following error being thrown in the journal monitor thread,
requiring a preferences reload to have journals continuing to be
processed:

```
Exception in thread Journal worker:
Traceback (most recent call last):
  File "/home/testing/.pyenv/versions/3.11.9/lib/python3.11/threading.py", line 1045, in _bootstrap_inner
    self.run()
  File "/home/testing/.pyenv/versions/3.11.9/lib/python3.11/threading.py", line 982, in run
    self._target(*self._args, **self._kwargs)
  File "/home/testing/gitstuff/EDMarketConnector/monitor.py", line 461, in worker
    self.root.event_generate('<<JournalEvent>>', when="tail")
  File "/home/testing/.pyenv/versions/3.11.9/lib/python3.11/tkinter/__init__.py", line 1933, in event_generate
    self.tk.call(args)
RuntimeError: main thread is not in main loop
```
2025-11-17 19:34:05 -05:00
David Sangrey ca243b775f [#261] Enable Plugin Enable/Disable System
This does rely on plugins respecting a stop call.
2025-11-17 19:12:12 -05:00