Updated .editorconfig to set indent_style=space and indent_size=4 for C# files. Reformat all .cs files to apply the new indentation settings. No code logic changes, just whitespace reformatting. also updated some comments in files in shorter and Precise way. No logic changes, just comment rewording for clarity and conciseness.
This commit is contained in:
+53
-166
@@ -90,10 +90,8 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
|
||||
public readonly WindowSystem WindowSystem = new(PluginName);
|
||||
|
||||
// v1.4.3: properties moved from { get; } to { get; private set; } = null!;
|
||||
// because LoadAsync now owns construction of the Phase-2 services.
|
||||
// Phase-1 services use the same shape for consistency, even though
|
||||
// they're still allocated in the ctor.
|
||||
// Phase-2 services are constructed in LoadAsync; null! shape is kept
|
||||
// consistent across all properties for clarity.
|
||||
public SettingsWindow SettingsWindow { get; private set; } = null!;
|
||||
public ChatLogWindow ChatLogWindow { get; private set; } = null!;
|
||||
public DbViewer DbViewer { get; private set; } = null!;
|
||||
@@ -115,27 +113,20 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
internal Ui.StatusBar StatusBar { get; private set; } = null!;
|
||||
internal Integrations.HonorificService HonorificService { get; private set; } = null!;
|
||||
|
||||
// (B3) Lightless idempotency guard — Dalamud may fire DisposeAsync twice
|
||||
// in a reload race; second call short-circuits.
|
||||
// Idempotency guard — Dalamud may fire DisposeAsync twice in a reload race.
|
||||
private int _disposeStarted;
|
||||
|
||||
internal int DeferredSaveFrames = -1;
|
||||
|
||||
// Serialises retention sweeps. The 24h auto-sweep on plugin load and
|
||||
// the manual button in the Privacy tab both run on background threads;
|
||||
// without this gate, hitting the manual button moments after a fresh
|
||||
// plugin start would launch two sweeps in parallel and the second one
|
||||
// would just re-do work the first one already finished. The lock guards
|
||||
// the flag — the flag check itself bails before we touch the database.
|
||||
// Volatile because the ImGui thread reads the flag outside the lock to
|
||||
// gate the manual button; without it the JIT may cache the value in a
|
||||
// register and miss the background-thread update.
|
||||
// Serialises retention sweeps so a manual trigger and the 24h auto-sweep
|
||||
// can't run in parallel. Volatile because the ImGui thread reads it outside
|
||||
// the lock to gate the manual button.
|
||||
internal readonly object RetentionSweepLock = new();
|
||||
internal volatile bool RetentionSweepRunning;
|
||||
|
||||
internal DateTime GameStarted { get; }
|
||||
|
||||
// Tab management needs to happen outside the chatlog window class for access reasons
|
||||
// Tab management lives here rather than in ChatLogWindow for access reasons.
|
||||
internal int LastTab { get; set; }
|
||||
internal int? WantedTab { get; set; }
|
||||
internal Tab CurrentTab
|
||||
@@ -149,31 +140,22 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
|
||||
public Plugin()
|
||||
{
|
||||
// Phase-1 ctor stays minimal: bootstrap-essentials only (conflict
|
||||
// gate, config load, language + ImGui init, WindowSystem skeleton).
|
||||
// Schema migrations and every service / window allocation moved to
|
||||
// LoadAsync so the sync ctor returns fast. On failure here nothing
|
||||
// is initialized yet, so just throw — there is nothing to clean up.
|
||||
// Phase-1 ctor: bootstrap-essentials only (conflict gate, config load,
|
||||
// language + ImGui init). All service/window allocation lives in LoadAsync.
|
||||
|
||||
// Refuse to start if upstream Chat 2 is loaded — prevents IPC
|
||||
// channel collisions and double-replacement of the in-game chat
|
||||
// window. Throwing here makes Dalamud abort the load cleanly with
|
||||
// our localized message instead of crashing FFXIV mid-frame.
|
||||
// Block load if upstream Chat 2 is active — prevents IPC collisions
|
||||
// and double-replacement of the in-game chat window.
|
||||
ChatTwoConflictDetector.ThrowIfChatTwoIsLoaded(Interface);
|
||||
|
||||
GameStarted = Process.GetCurrentProcess().StartTime.ToUniversalTime();
|
||||
|
||||
// Hellion Chat: take over config + database from upstream ChatTwo
|
||||
// before Dalamud loads our plugin config. Idempotent: only acts on
|
||||
// the first start where the legacy paths exist and ours don't.
|
||||
// Migrate config + database from upstream ChatTwo on first start.
|
||||
MigrateFromChatTwoLayout();
|
||||
|
||||
Config = Interface.GetPluginConfig() as Configuration ?? new Configuration();
|
||||
|
||||
// Schema-gate: v1.4.3 only supports config schema v16. Older configs
|
||||
// went through their migrations in v1.2.1 (v15→v16) and earlier; users
|
||||
// who skipped past those releases need to install v1.4.2 first to run
|
||||
// the migration chain, then upgrade to v1.4.3.
|
||||
// Schema gate: v1.4.3 requires config v16. Users on older schemas
|
||||
// must install v1.4.2 first to run the migration chain.
|
||||
if (Config.Version < 16)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
@@ -182,19 +164,13 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
);
|
||||
}
|
||||
|
||||
// Hellion Chat — Auto-Tell-Tabs Defense-in-Depth. SaveConfig
|
||||
// already strips temp tabs before persistence, but a previous
|
||||
// crash or external write could have left them in the JSON.
|
||||
// Drop them on load to guarantee the session-only invariant.
|
||||
// Drop session-only Auto-Tell-Tabs that a previous crash may have persisted.
|
||||
Config.Tabs.RemoveAll(t => t.IsTempTab);
|
||||
|
||||
LanguageChanged(Interface.UiLanguage);
|
||||
ImGuiUtil.Initialize(this);
|
||||
|
||||
DeferredSaveFrames = -1;
|
||||
|
||||
// WindowSystem skeleton is initialised by the readonly field above —
|
||||
// no AddWindow yet; window construction lives in LoadAsync.
|
||||
}
|
||||
|
||||
public async Task LoadAsync(CancellationToken cancellationToken)
|
||||
@@ -203,14 +179,8 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
|
||||
try
|
||||
{
|
||||
// Hellion v1.0.0 default tab layout. Five thematically separated
|
||||
// tabs: General catches the immediate-surroundings public chat
|
||||
// (Say/Yell/Shout) only; System absorbs the rest of the technical
|
||||
// and gameplay-event noise; FreeCompany, Group and Linkshell each
|
||||
// own their respective channel set. Tells are not in a static
|
||||
// tab anymore — Auto-Tell-Tabs spawns dedicated per-conversation
|
||||
// tabs on demand. Novice-Network gets no preset tab; users who
|
||||
// want it can add HellionBeginner from Settings → Tabs.
|
||||
// Default tab layout on fresh install. Tells are handled by
|
||||
// Auto-Tell-Tabs; Novice Network has no preset tab by design.
|
||||
if (Config.Tabs.Count == 0)
|
||||
{
|
||||
Config.Tabs.Add(TabsUtil.VanillaGeneral);
|
||||
@@ -222,19 +192,12 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Sync allocation + handle registration. BuildFonts() registers
|
||||
// IFontHandles with Dalamud's UiBuilder.FontAtlas — registration
|
||||
// itself is non-blocking (handles stored, lambdas queued). Dalamud
|
||||
// rebuilds the atlas on its own pipeline a few frames later; first
|
||||
// frames render with the default font until the rebuild lands and
|
||||
// ImGui switches to Hellion-Exo2 / NotoSans (visible "font-pop").
|
||||
// Mirrors ChatTwo Plugin.cs:152.
|
||||
// BuildFonts registers handles with Dalamud's FontAtlas; the atlas
|
||||
// rebuilds async a few frames later (visible "font-pop" on first load).
|
||||
FontManager = new FontManager();
|
||||
FontManager.BuildFonts();
|
||||
|
||||
// Theme init stays sync on the LoadAsync continuation — cheap,
|
||||
// and Active is read every Draw frame, so the registry must be
|
||||
// wired before the first hook fires.
|
||||
// ThemeRegistry must be wired before the first Draw tick.
|
||||
var customThemesDir = Path.Combine(Interface.ConfigDirectory.FullName, "themes");
|
||||
Directory.CreateDirectory(customThemesDir);
|
||||
SeedExampleThemeIfEmpty(customThemesDir);
|
||||
@@ -243,11 +206,9 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Service allocations: order encodes dependencies. Commands is
|
||||
// alloc-only here; Initialise() runs after windows exist so the
|
||||
// slash-commands can toggle their visibility. HonorificService
|
||||
// registers IPC subscribers up-front so Ready/Disposing events
|
||||
// are caught from the very first frame.
|
||||
// Service allocations — order encodes dependencies.
|
||||
// HonorificService registers IPC subscribers early to catch
|
||||
// Ready/Disposing events from the first frame.
|
||||
FileDialogManager = new FileDialogManager();
|
||||
Commands = new Commands();
|
||||
Functions = new GameFunctions.GameFunctions(this);
|
||||
@@ -258,9 +219,6 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
StatusBar = new Ui.StatusBar();
|
||||
MessageManager = new MessageManager(this);
|
||||
|
||||
// Auto-Tell-Tabs subscribes to MessageManager.MessageProcessed for
|
||||
// live tells and to ClientState.Logout for cleanup; needs the live
|
||||
// store handed in at construction.
|
||||
AutoTellTabsService = new AutoTellTabsService(
|
||||
this,
|
||||
MessageManager,
|
||||
@@ -268,7 +226,6 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
);
|
||||
AutoTellTabsService.Initialize();
|
||||
|
||||
// SelfTest steps poll Active per frame and need the registry wired.
|
||||
SelfTestRegistry.RegisterTestSteps([new SelfTests.ThemeSwitchSelfTestStep(this)]);
|
||||
|
||||
ChatLogWindow = new ChatLogWindow(this);
|
||||
@@ -289,22 +246,19 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
WindowSystem.AddWindow(DebuggerWindow);
|
||||
WindowSystem.AddWindow(FirstRunWizard);
|
||||
|
||||
// Open the wizard on a fresh install. Existing ChatTwo users have
|
||||
// FirstRunCompleted set to true by the v6→v7 migration above.
|
||||
if (!Config.FirstRunCompleted)
|
||||
FirstRunWizard.IsOpen = true;
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// let all the other components register, then initialize commands
|
||||
Commands.Initialise();
|
||||
|
||||
// Daily retention sweep, fire-and-forget. Skips itself when
|
||||
// disabled or when it already ran within the past 24 hours.
|
||||
// Daily retention sweep — fire-and-forget, skips when disabled
|
||||
// or already ran within the past 24 hours.
|
||||
RunRetentionSweepIfDue();
|
||||
|
||||
if (Config.ShowEmotes)
|
||||
_ = EmoteCache.LoadData(); // Fire-and-forget, exceptions caught inside
|
||||
_ = EmoteCache.LoadData();
|
||||
|
||||
if (Interface.Reason is not PluginLoadReason.Boot)
|
||||
MessageManager.FilterAllTabsAsync();
|
||||
@@ -313,33 +267,22 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
Interface.UiBuilder.DisableGposeUiHide = true;
|
||||
|
||||
#if !DEBUG
|
||||
// Fire-and-forget on a worker thread. The first auto-translate use of
|
||||
// a session may have a sub-second hitch if the cache hasn't filled yet,
|
||||
// but that's preferable to making every user wait ~300 ms during
|
||||
// plugin load for a cache they may never touch. ChatTwo (upstream)
|
||||
// does this sync; we trade load-time for first-use latency.
|
||||
// Fire-and-forget — first auto-translate use may have a sub-second
|
||||
// hitch if the cache hasn't filled yet, but avoids blocking load.
|
||||
_ = Task.Run(AutoTranslate.PreloadCache, cancellationToken);
|
||||
#endif
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// (B1) Hooks last: every service and window must be live before
|
||||
// Dalamud fires our first Draw / FrameworkUpdate tick. Anything
|
||||
// earlier risks rendering against null FontManager / ThemeRegistry.
|
||||
// Hooks last — all services and windows must be live before
|
||||
// the first Draw / FrameworkUpdate tick fires.
|
||||
Framework.Update += FrameworkUpdate;
|
||||
Interface.UiBuilder.Draw += Draw;
|
||||
Interface.LanguageChanged += LanguageChanged;
|
||||
// Hellion Chat — surface a "main UI" entry point so Dalamud's
|
||||
// plugin list shows the Open-Plugin button. Settings is the
|
||||
// most useful landing place; OpenConfigUi is already wired to
|
||||
// the same toggle inside SettingsWindow.
|
||||
Interface.UiBuilder.OpenMainUi += OpenMainUi;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Mirror the v1.4.0 load-failure recovery: hand off to DisposeAsync
|
||||
// so partially-built services are torn down. Swallow the cleanup
|
||||
// exception so the original load failure stays the visible cause.
|
||||
try
|
||||
{
|
||||
await DisposeAsync().ConfigureAwait(false);
|
||||
@@ -351,28 +294,22 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
}
|
||||
}
|
||||
|
||||
// Suppressing this warning because DisposeAsync may run after a partial
|
||||
// LoadAsync, so some properties may not be initialized.
|
||||
[SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract")]
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
// (B3) Idempotency guard — Dalamud may reload-race us; second
|
||||
// call short-circuits so we don't double-dispose services.
|
||||
// Idempotency guard — second call short-circuits on reload race.
|
||||
if (Interlocked.Exchange(ref _disposeStarted, 1) != 0)
|
||||
return;
|
||||
|
||||
Exception? failure = null;
|
||||
|
||||
// Hooks unsubscribe FIRST so no Draw / FrameworkUpdate / LanguageChanged
|
||||
// tick can fire while we're tearing services down. Mirrors the
|
||||
// hooks-last subscribe order in LoadAsync.
|
||||
// Unsubscribe hooks first — mirrors the hooks-last subscribe order in LoadAsync.
|
||||
failure = CaptureFailure(failure, () => Interface.UiBuilder.OpenMainUi -= OpenMainUi);
|
||||
failure = CaptureFailure(failure, () => Interface.LanguageChanged -= LanguageChanged);
|
||||
failure = CaptureFailure(failure, () => Interface.UiBuilder.Draw -= Draw);
|
||||
failure = CaptureFailure(failure, () => Framework.Update -= FrameworkUpdate);
|
||||
|
||||
// v1.4.0 F5.3 — flush a pending DeferredSave before service teardown,
|
||||
// since FrameworkUpdate just got unsubscribed and won't fire it.
|
||||
// Flush a pending DeferredSave — FrameworkUpdate won't fire it anymore.
|
||||
failure = CaptureFailure(
|
||||
failure,
|
||||
() =>
|
||||
@@ -385,13 +322,10 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
}
|
||||
);
|
||||
|
||||
// Auto-Tell-Tabs unsubscribes from MessageProcessed before MessageManager
|
||||
// goes away. Pure-memory cleanup, no framework-thread requirement.
|
||||
// Unsubscribe AutoTellTabs before MessageManager goes away.
|
||||
failure = CaptureFailure(failure, () => AutoTellTabsService?.Dispose());
|
||||
|
||||
// v1.4.0 F6.2 — MessageManager has its own async dispose path
|
||||
// (DB flush, pending-message thread shutdown). Run it before the
|
||||
// framework-block so the worker threads are quiesced first.
|
||||
// MessageManager has its own async dispose path (DB flush, thread shutdown).
|
||||
if (MessageManager is not null)
|
||||
{
|
||||
failure = await CaptureFailureAsync(
|
||||
@@ -401,36 +335,24 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// (B4) Game-Function / IPC / UI-Window cleanup MUST run on the
|
||||
// framework thread. WindowSystem mutations and IPC subscriber
|
||||
// disposes touch Dalamud state that's only safe from the framework.
|
||||
// Worker-thread DisposeAsync would race the next Draw tick.
|
||||
// Per-line CaptureFailure so a single throw can't strand the lines
|
||||
// behind it; mirrors Lightless DisposeFrameworkBoundServicesAsync.
|
||||
// Game-function / IPC / window cleanup must run on the framework thread.
|
||||
try
|
||||
{
|
||||
await Framework
|
||||
.RunOnFrameworkThread(() =>
|
||||
{
|
||||
// Game-Functions first — other services may still query
|
||||
// chat-interactable state during their Dispose.
|
||||
failure = CaptureFailure(
|
||||
failure,
|
||||
() => GameFunctions.GameFunctions.SetChatInteractable(true)
|
||||
);
|
||||
|
||||
// IPC subscribers — dispose before windows so any final
|
||||
// event firing from the IPC source can't reach a half-torn
|
||||
// ChatLogWindow.
|
||||
// IPC subscribers before windows — prevents a final IPC event
|
||||
// from reaching a half-torn ChatLogWindow.
|
||||
failure = CaptureFailure(failure, () => HonorificService?.Dispose());
|
||||
failure = CaptureFailure(failure, () => TypingIpc?.Dispose());
|
||||
failure = CaptureFailure(failure, () => ExtraChat?.Dispose());
|
||||
failure = CaptureFailure(failure, () => Ipc?.Dispose());
|
||||
|
||||
// Windows — RemoveAllWindows first, then per-window Dispose.
|
||||
// Order matches the pre-v1.4.3 Dispose body byte-for-byte.
|
||||
// CommandHelpWindow and FirstRunWizard don't implement
|
||||
// IDisposable; their resources are reclaimed via WindowSystem.
|
||||
failure = CaptureFailure(failure, () => WindowSystem?.RemoveAllWindows());
|
||||
failure = CaptureFailure(failure, () => ChatLogWindow?.Dispose());
|
||||
failure = CaptureFailure(failure, () => DbViewer?.Dispose());
|
||||
@@ -446,8 +368,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
failure ??= ex;
|
||||
}
|
||||
|
||||
// Pure-memory cleanups — no Framework / UI / IPC touch, so they
|
||||
// run on whatever thread DisposeAsync resumes on.
|
||||
// Pure-memory cleanups — no Framework / UI / IPC touch.
|
||||
failure = CaptureFailure(failure, () => Functions?.Dispose());
|
||||
failure = CaptureFailure(failure, () => Commands?.Dispose());
|
||||
failure = CaptureFailure(failure, () => EmoteCache.Dispose());
|
||||
@@ -456,9 +377,8 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
ExceptionDispatchInfo.Capture(failure).Throw();
|
||||
}
|
||||
|
||||
// Lightless-pattern capture helpers: run cleanup, remember the FIRST
|
||||
// exception, keep going. Without these one mid-teardown failure would
|
||||
// skip every cleanup behind it and leave services half-torn.
|
||||
// Run cleanup actions individually so a single failure doesn't strand
|
||||
// the remaining teardown steps.
|
||||
private static Exception? CaptureFailure(Exception? failure, Action action)
|
||||
{
|
||||
try
|
||||
@@ -499,9 +419,6 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
var ourConfigFile = Path.Combine(pluginConfigsDir, "HellionChat.json");
|
||||
var ourConfigDir = Interface.ConfigDirectory.FullName;
|
||||
|
||||
// Track whether anything legitimately blocked us. The most common
|
||||
// cause is upstream Chat 2 still being loaded — its SQLite handle
|
||||
// keeps chat-sqlite.db locked and File.Move throws IOException.
|
||||
var lockedBlocker = false;
|
||||
|
||||
try
|
||||
@@ -523,13 +440,6 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
lockedBlocker = true;
|
||||
}
|
||||
|
||||
// The plugin's ConfigDirectory may already exist on first load
|
||||
// (Dalamud creates it), so check at the file level instead of
|
||||
// skipping when the directory is present. Move every legacy
|
||||
// entry whose target name is not occupied yet, then remove the
|
||||
// source dir if it ends up empty. Each move is wrapped on its
|
||||
// own so a single locked file (the SQLite db while ChatTwo still
|
||||
// runs) does not abandon the rest of the migration.
|
||||
if (!Directory.Exists(legacyConfigDir))
|
||||
return;
|
||||
|
||||
@@ -537,6 +447,8 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
{
|
||||
Directory.CreateDirectory(ourConfigDir);
|
||||
|
||||
// Move each file individually so a single locked file (e.g. the
|
||||
// SQLite db while ChatTwo is still loaded) doesn't abort the rest.
|
||||
foreach (var file in Directory.EnumerateFiles(legacyConfigDir))
|
||||
{
|
||||
var target = Path.Combine(ourConfigDir, Path.GetFileName(file));
|
||||
@@ -590,9 +502,6 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
|
||||
if (lockedBlocker)
|
||||
{
|
||||
// Surface the most common cause to the user as a notification
|
||||
// so they don't think Hellion Chat lost their history when in
|
||||
// fact upstream Chat 2 was still holding the database file.
|
||||
Notification.AddNotification(
|
||||
new Dalamud.Interface.ImGuiNotification.Notification
|
||||
{
|
||||
@@ -610,10 +519,6 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
|
||||
private void OpenMainUi()
|
||||
{
|
||||
// Settings is the most useful landing surface — same target as the
|
||||
// Configure button. SettingsWindow.Toggle is internal and already
|
||||
// wired to OpenConfigUi, so re-using IsOpen keeps both entry points
|
||||
// behaviourally identical.
|
||||
SettingsWindow.IsOpen = !SettingsWindow.IsOpen;
|
||||
}
|
||||
|
||||
@@ -624,8 +529,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
if (DateTimeOffset.UtcNow - Config.RetentionLastRunAt < TimeSpan.FromHours(24))
|
||||
return;
|
||||
|
||||
// Snapshot the policy so the user can edit settings while we run.
|
||||
// Spec defaults form the baseline; explicit user overrides win.
|
||||
// Snapshot the policy so the user can edit settings while the sweep runs.
|
||||
var policy = new Dictionary<int, int>();
|
||||
foreach (var (type, days) in Privacy.PrivacyDefaults.DefaultRetentionDays)
|
||||
policy[(int)(ushort)type] = days;
|
||||
@@ -633,16 +537,10 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
policy[(int)(ushort)type] = days;
|
||||
var defaultDays = Config.RetentionDefaultDays;
|
||||
|
||||
// IsBackground = true for the same reason as PendingMessageThread:
|
||||
// a stuck sweep must never block plugin unload. RunRetentionSweepIfDue
|
||||
// guards the run-frequency, and the sweep itself uses the framework's
|
||||
// cooperative cancellation pattern. The background flag is the safety
|
||||
// net if the sweep ever takes longer than expected.
|
||||
// IsBackground = true so a stuck sweep never blocks plugin unload.
|
||||
new Thread(() =>
|
||||
{
|
||||
// Bail out cheaply if a manual sweep is already in flight; the
|
||||
// lock around the actual work would queue us up otherwise and
|
||||
// we would just re-do whatever the manual run already did.
|
||||
// Bail early if a manual sweep is already in flight.
|
||||
lock (RetentionSweepLock)
|
||||
{
|
||||
if (RetentionSweepRunning)
|
||||
@@ -659,11 +557,8 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
if (deleted > 0)
|
||||
{
|
||||
Log.Information($"Retention sweep deleted {deleted} expired messages.");
|
||||
// Run the clear+refilter synchronously on the framework thread.
|
||||
// Earlier this called FilterAllTabsAsync(), which is fire-and-forget
|
||||
// — the .Wait() here would return as soon as the inner Task.Run was
|
||||
// dispatched, racing the next sweep cycle against the still-running
|
||||
// filter pass. See AUDIT-2026-05-05 [QUAL-02].
|
||||
// Run clear+refilter on the framework thread — FilterAllTabsAsync
|
||||
// is fire-and-forget and would race the next sweep cycle.
|
||||
Framework
|
||||
.Run(() =>
|
||||
{
|
||||
@@ -694,9 +589,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
|
||||
private void Draw()
|
||||
{
|
||||
// Theme-Engine ist ab v14 immer aktiv; Klassik ist jetzt ein eigenes
|
||||
// Theme statt einem deaktivierten Hellion-Theme. Active wird einmal
|
||||
// pro Frame aus der Registry gelesen.
|
||||
// Theme engine is always active; Classic is a theme, not a disabled state.
|
||||
using IDisposable _style = HellionStyle.PushGlobal(
|
||||
ThemeRegistry.Active,
|
||||
Config.WindowOpacity
|
||||
@@ -711,9 +604,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
return;
|
||||
}
|
||||
|
||||
// v1.0.2 — global skip while the New Game+ menu (QuestRedo addon) is
|
||||
// open. Hides every plugin window in one shot (chat log, pop-outs,
|
||||
// settings, db viewer, etc.), matching the LoadingScreens pattern.
|
||||
// Hide all plugin windows while the New Game+ menu is open.
|
||||
if (
|
||||
Config.HideInNewGamePlusMenu
|
||||
&& GameFunctions.GameFunctions.IsAddonInteractable(
|
||||
@@ -742,10 +633,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
|
||||
internal void SaveConfig()
|
||||
{
|
||||
// Hellion Chat — Auto-Tell-Tabs are session-only. Strip them out
|
||||
// before serialization so a crash mid-session can never persist
|
||||
// them. We snapshot the full tab list first and restore it after
|
||||
// the save, preserving the user's order and open conversations.
|
||||
// Strip session-only Auto-Tell-Tabs before serialization; restore after.
|
||||
var snapshot = Config.Tabs.ToList();
|
||||
Config.Tabs.RemoveAll(t => t.IsTempTab);
|
||||
|
||||
@@ -794,9 +682,8 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
Condition[ConditionFlag.OccupiedInCutSceneEvent]
|
||||
|| Condition[ConditionFlag.WatchingCutscene78];
|
||||
|
||||
// v1.1.0 — wenn der themes/-Ordner leer ist, schreiben wir die embedded
|
||||
// example-theme.json als Vorlage rein. Bestehende User-Customs werden
|
||||
// nicht angefasst (existing JSONs lassen den Block überspringen).
|
||||
// Seeds example-theme.json into the themes dir on first run.
|
||||
// Skipped if any custom JSON already exists.
|
||||
private static void SeedExampleThemeIfEmpty(string dir)
|
||||
{
|
||||
if (Directory.EnumerateFiles(dir, "*.json").Any())
|
||||
|
||||
Reference in New Issue
Block a user