chore: comments, second pass -- the task codes the first pass missed
The first sweep matched a character class that swallowed the digit, so a bare B1 slipped through while B1-2 was caught. Searching the whole A-Z space instead of guessing prefixes turned up 130-odd more: B0 through B6, C2, C3, D1, H2, M6, P7, P8, T2, W2, plus GP-04, KB-01, OD-1, PM-1, PM-3, SEC-01, TR-4, TR-7, UI-11, UI-12, XC-8 and API-3. Kept deliberately: 41 B4 01 is a byte signature, "N0" a format string, #L119-L128 a source anchor, LS4/LS6 are linkshells, and A=FF B=0C G=41 R=C2 explains a colour-channel order. Those look like codes and are not. Also translated the eight German comments left in the theme files and ImGuiUtil. Seven of them described what a palette does to which channel, which is worth reading -- just not in a second language in an otherwise English codebase.
This commit is contained in:
+134
-67
@@ -134,7 +134,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
internal Integrations.HonorificService HonorificService { get; private set; } = null!;
|
||||
internal Integrations.CustomAudioPlayer CustomAudioPlayer { get; private set; } = null!;
|
||||
|
||||
// Ctor-smoke anchors (B0-2). Exposed so the Payload/Chunk ctor-smoke steps
|
||||
// Ctor-smoke anchors. Exposed so the Payload/Chunk ctor-smoke steps
|
||||
// can drive the real per-frame Lender path (Borrow()) and the eager
|
||||
// singletons through the container, never via new(). Mirror of the
|
||||
// FontManager property pattern — every SelfTest reaches services this way.
|
||||
@@ -178,7 +178,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
internal bool ChatActivationRequested;
|
||||
|
||||
// Set in the first DisposeAsync statement so async callbacks scheduled
|
||||
// via Framework.RunOnTick (v1.4.8 B3 retention sweep) can early-bail
|
||||
// via Framework.RunOnTick (v1.4.8 retention sweep) can early-bail
|
||||
// before they touch state that has already been torn down. Volatile
|
||||
// because the tick reads it from a different thread than the writer.
|
||||
private volatile bool _isDisposing;
|
||||
@@ -188,7 +188,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
// just unloaded belongs to nobody.
|
||||
internal bool IsDisposing => _isDisposing;
|
||||
|
||||
// v1.9.0 B5: last full Draw() wall-time in ms, written once per frame at
|
||||
// v1.9.0: last full Draw() wall-time in ms, written once per frame at
|
||||
// the end of the UiBuilder.Draw handler. Covers the GlobalStyleScope push
|
||||
// and the font push (the first-frame hitch measurement must include atlas/style
|
||||
// prologue cost), not just WindowSystem.Draw — measuring the inner call
|
||||
@@ -214,7 +214,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
// must never block doing so.
|
||||
internal readonly Util.DbOperationGate DbOperations = new();
|
||||
|
||||
// B3: neutral owner of the Config.Tabs LIST-structure lock so both the
|
||||
// Neutral owner of the Config.Tabs LIST-structure lock so both the
|
||||
// worker-thread mutator (AutoTellTabsService) and the framework-thread
|
||||
// refilter (MessageManager) share ONE monitor. Lock order: this outer,
|
||||
// MessageList's SemaphoreSlim inner — never the reverse.
|
||||
@@ -287,83 +287,118 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
+ "Please install v1.4.2 first to migrate the configuration, then upgrade to v1.4.10."
|
||||
);
|
||||
}
|
||||
// v23 migration: SidebarTabView was the 1.5.6 sidebar↔top-tabs switch,
|
||||
// superseded by MainWindowLayoutMode in the v1.6.0 rewrite. A user who
|
||||
// set it false (only effective in 1.5.6) wanted top tabs — carry that
|
||||
// intent forward. Runs only for pre-v23 configs; fresh configs load at
|
||||
// LatestVersion and skip it. Additive v20/v22 fields keep their
|
||||
// initializer defaults as before.
|
||||
if (Config.Version < 23 && !Config.SidebarTabView)
|
||||
// 2.0.0 does not migrate, it starts over. Five cycles rebuilt the whole
|
||||
// window layer, and a config carried through them keeps values chosen
|
||||
// against surfaces that no longer exist -- an opacity picked for a
|
||||
// window that has been redrawn twice since, tabs laid out for a sidebar
|
||||
// that works differently now. Every user of this build is a tester who
|
||||
// was told this happens, and it is the only way to be sure everyone
|
||||
// sees the same defaults.
|
||||
//
|
||||
// The file is copied aside first. Rolling back to 1.5.6 is a supported
|
||||
// move here and it stays cheap: the old settings are a file copy away,
|
||||
// rather than an evening of clicking them back in.
|
||||
if (Config.Version < 27)
|
||||
{
|
||||
Config.MainWindowLayoutMode = MainWindowLayoutMode.TopTabs;
|
||||
}
|
||||
BackUpConfigBeforeReset();
|
||||
|
||||
Config = Configuration.CreateFresh();
|
||||
Config.Version = 27;
|
||||
|
||||
// Saved immediately: a crash between here and the first user-driven
|
||||
// save would otherwise run the whole reset again on the next start,
|
||||
// and the second run would back up the already-reset file over the
|
||||
// real backup.
|
||||
SaveConfig();
|
||||
|
||||
// v24 migration: the privacy filter used to route a known but unticked
|
||||
// channel through the unknown-type failsafe, so the channel grid was
|
||||
// inert whenever that failsafe was on. Corrected in v1.12.0. A config
|
||||
// that never picked a channel was storing everything through that hole,
|
||||
// and the corrected rule would store nothing at all -- so the intent is
|
||||
// carried forward as a filter that is honestly switched off.
|
||||
if (
|
||||
Config.Version < 24
|
||||
&& Privacy.StorageRule.ShouldDisableFilterOnV24(
|
||||
Config.PrivacyFilterEnabled,
|
||||
Config.PrivacyPersistUnknownChannels,
|
||||
Config.PrivacyPersistChannels.Count
|
||||
)
|
||||
)
|
||||
{
|
||||
Config.PrivacyFilterEnabled = false;
|
||||
// Log, not LogProxy: this runs in Phase-0 and the proxy is only
|
||||
// resolved from the container further down.
|
||||
Log.Information(
|
||||
"Privacy filter switched off during the v24 migration: it was on with no channels "
|
||||
+ "picked, which stored everything through the unknown-channel failsafe. Pick "
|
||||
+ "channels in Settings to switch it back on."
|
||||
"Config reset to defaults for 2.0.0. Previous settings kept as "
|
||||
+ "HellionChat.json.pre-2.0.0.bak next to the config file."
|
||||
);
|
||||
}
|
||||
|
||||
// v25 carried no migration step; the bump was documentation.
|
||||
//
|
||||
// v26 does. NameCameFromPartner is what screenshot mode reads to decide
|
||||
// whether a tab name is a person, and a config written before it existed
|
||||
// has it false on every tab -- including pinned tell tabs, which survive
|
||||
// reloads and are named "Player@World". Anything still carrying a tell
|
||||
// binding or the temp flag got its name from a partner, so the flag is
|
||||
// set from those two.
|
||||
//
|
||||
// Tabs promoted before this version are past saving: promotion clears
|
||||
// both markers and keeps the name, so nothing in the stored data says
|
||||
// where that name came from. Renaming one clears the flag anyway, which
|
||||
// is the same outcome the user gets by editing it.
|
||||
if (Config.Version < 26)
|
||||
else
|
||||
{
|
||||
var carried = 0;
|
||||
foreach (var tab in Config.Tabs)
|
||||
// v23 migration: SidebarTabView was the 1.5.6 sidebar↔top-tabs switch,
|
||||
// superseded by MainWindowLayoutMode in the v1.6.0 rewrite. A user who
|
||||
// set it false (only effective in 1.5.6) wanted top tabs — carry that
|
||||
// intent forward. Runs only for pre-v23 configs; fresh configs load at
|
||||
// LatestVersion and skip it. Additive v20/v22 fields keep their
|
||||
// initializer defaults as before.
|
||||
if (Config.Version < 23 && !Config.SidebarTabView)
|
||||
{
|
||||
if (tab.NameCameFromPartner || (!tab.IsTempTab && tab.TellTarget?.IsSet() != true))
|
||||
continue;
|
||||
|
||||
tab.NameCameFromPartner = true;
|
||||
carried++;
|
||||
Config.MainWindowLayoutMode = MainWindowLayoutMode.TopTabs;
|
||||
}
|
||||
|
||||
if (carried > 0)
|
||||
// v24 migration: the privacy filter used to route a known but unticked
|
||||
// channel through the unknown-type failsafe, so the channel grid was
|
||||
// inert whenever that failsafe was on. Corrected in v1.12.0. A config
|
||||
// that never picked a channel was storing everything through that hole,
|
||||
// and the corrected rule would store nothing at all -- so the intent is
|
||||
// carried forward as a filter that is honestly switched off.
|
||||
if (
|
||||
Config.Version < 24
|
||||
&& Privacy.StorageRule.ShouldDisableFilterOnV24(
|
||||
Config.PrivacyFilterEnabled,
|
||||
Config.PrivacyPersistUnknownChannels,
|
||||
Config.PrivacyPersistChannels.Count
|
||||
)
|
||||
)
|
||||
{
|
||||
Config.PrivacyFilterEnabled = false;
|
||||
// Log, not LogProxy: this runs in Phase-0 and the proxy is only
|
||||
// resolved from the container further down.
|
||||
Log.Information(
|
||||
$"Marked {carried} tab(s) as partner-named during the v26 migration, so "
|
||||
+ "screenshot mode hides them in the channel header."
|
||||
"Privacy filter switched off during the v24 migration: it was on with no channels "
|
||||
+ "picked, which stored everything through the unknown-channel failsafe. Pick "
|
||||
+ "channels in Settings to switch it back on."
|
||||
);
|
||||
}
|
||||
|
||||
// v25 carried no migration step; the bump was documentation.
|
||||
//
|
||||
// v26 does. NameCameFromPartner is what screenshot mode reads to decide
|
||||
// whether a tab name is a person, and a config written before it existed
|
||||
// has it false on every tab -- including pinned tell tabs, which survive
|
||||
// reloads and are named "Player@World". Anything still carrying a tell
|
||||
// binding or the temp flag got its name from a partner, so the flag is
|
||||
// set from those two.
|
||||
//
|
||||
// Tabs promoted before this version are past saving: promotion clears
|
||||
// both markers and keeps the name, so nothing in the stored data says
|
||||
// where that name came from. Renaming one clears the flag anyway, which
|
||||
// is the same outcome the user gets by editing it.
|
||||
if (Config.Version < 26)
|
||||
{
|
||||
var carried = 0;
|
||||
foreach (var tab in Config.Tabs)
|
||||
{
|
||||
if (
|
||||
tab.NameCameFromPartner
|
||||
|| (!tab.IsTempTab && tab.TellTarget?.IsSet() != true)
|
||||
)
|
||||
continue;
|
||||
|
||||
tab.NameCameFromPartner = true;
|
||||
carried++;
|
||||
}
|
||||
|
||||
if (carried > 0)
|
||||
{
|
||||
Log.Information(
|
||||
$"Marked {carried} tab(s) as partner-named during the v26 migration, so "
|
||||
+ "screenshot mode hides them in the channel header."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Config.Version = 26;
|
||||
Config.Version = 27;
|
||||
|
||||
// Unpinned TempTabs are session-only and dropped on every load. Pinned
|
||||
// TempTabs survive reload -- tester feedback in v1.4.7.
|
||||
Config.Tabs.RemoveAll(TabLifecycleHelpers.ShouldStripOnLoad);
|
||||
|
||||
// GP-04: clear stale Tab.PopOut flags now — the pool binds further down
|
||||
// Clear stale Tab.PopOut flags now — the pool binds further down
|
||||
// (ChannelPopoutPool resolve below), so at this point no tab can own a
|
||||
// slot. A persisted PopOut=true (notably on surviving pinned TempTabs)
|
||||
// would otherwise be a flag with no window. Runs after the strip, before
|
||||
@@ -462,7 +497,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
FirstRunWizard = _host.Services.GetRequiredService<FirstRunWizard>();
|
||||
ChannelPopoutPool = _host.Services.GetRequiredService<Ui.Windows.ChannelPopoutPool>();
|
||||
|
||||
// Ctor-smoke anchors (B0-2). Resolved last, against the fully built
|
||||
// Ctor-smoke anchors. Resolved last, against the fully built
|
||||
// container: every MakePayloadHandler dep (MainWindow, InputBar,
|
||||
// ChunkRenderer, ...) is resolvable here, and the ChunkRenderer resolve
|
||||
// below just reuses the same cached singleton. These are plain
|
||||
@@ -732,7 +767,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
return;
|
||||
|
||||
// Set before any cleanup so deferred Framework.RunOnTick callbacks
|
||||
// (B3 retention sweep) see the flag and bail out before they touch
|
||||
// (the retention sweep) see the flag and bail out before they touch
|
||||
// MessageManager / Log / static fields that the rest of this method
|
||||
// is about to tear down.
|
||||
_isDisposing = true;
|
||||
@@ -840,6 +875,38 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
return failure;
|
||||
}
|
||||
|
||||
// Copies the config aside before the 2.0.0 reset overwrites it. Best effort
|
||||
// by design: a backup that cannot be written must not stop the plugin from
|
||||
// starting, and the reset itself is what the user was told would happen.
|
||||
//
|
||||
// Overwrite=false, so a second run cannot bury the real backup under a copy
|
||||
// of the already-reset file. The reset saves immediately for the same
|
||||
// reason, but a crash in between is exactly when this matters.
|
||||
private static void BackUpConfigBeforeReset()
|
||||
{
|
||||
try
|
||||
{
|
||||
var dir = Interface.ConfigDirectory.Parent?.FullName;
|
||||
if (dir is null)
|
||||
return;
|
||||
|
||||
var configFile = Path.Combine(dir, "HellionChat.json");
|
||||
if (!File.Exists(configFile))
|
||||
return;
|
||||
|
||||
var backup = Path.Combine(dir, "HellionChat.json.pre-2.0.0.bak");
|
||||
if (File.Exists(backup))
|
||||
return;
|
||||
|
||||
File.Copy(configFile, backup);
|
||||
Log.Information($"HellionChat: config backed up to {backup} before the 2.0.0 reset");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warning(e, "HellionChat: could not back up the config before the 2.0.0 reset");
|
||||
}
|
||||
}
|
||||
|
||||
private static void MigrateFromChatTwoLayout()
|
||||
{
|
||||
var pluginConfigsDir = Interface.ConfigDirectory.Parent?.FullName;
|
||||
@@ -1181,7 +1248,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
// Schedule on the next framework tick to avoid the ~194ms
|
||||
// hitch from blocking with .Wait() while the frame finishes.
|
||||
// The Config.Tabs enumeration in ClearAllTabs/FilterAllTabs is
|
||||
// now guarded by the shared Plugin.TabsListLock (B3), so this
|
||||
// now guarded by the shared Plugin.TabsListLock, so this
|
||||
// tick scheduling is purely hitch-avoidance, not safety.
|
||||
// Pattern reference: SimpleTweaks
|
||||
// Tweaks/Chat/CaseInsensitiveCommands.cs:45.
|
||||
@@ -1268,7 +1335,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
|
||||
private void Draw()
|
||||
{
|
||||
// v1.9.0 B5: time the whole handler (style + font prologue included).
|
||||
// v1.9.0: time the whole handler (style + font prologue included).
|
||||
// Bail before measuring once teardown has begun — a late Draw tick
|
||||
// must not touch ThemeRegistry / FontManager after DisposeAsync.
|
||||
if (_isDisposing)
|
||||
@@ -1277,7 +1344,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
var drawWatch = Stopwatch.StartNew();
|
||||
try
|
||||
{
|
||||
// v1.4.8 B2: pick up external edits of the active custom theme JSON
|
||||
// v1.4.8: pick up external edits of the active custom theme JSON
|
||||
// without forcing the user to re-click the picker. The disk-stat is
|
||||
// 1Hz-throttled inside RefreshActiveIfStale, so this is essentially
|
||||
// free on built-in themes and ~1 stat/second on custom themes.
|
||||
@@ -1367,7 +1434,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
// Config.Tabs across the save so JSON includes them. Cloning only the
|
||||
// unpinned subset keeps the allocation proportional to
|
||||
// AutoTellTabsLimit (<=15) instead of the full tab list.
|
||||
// B3: the strip/restore mutates the tab LIST, so it shares TabsListLock
|
||||
// The strip/restore mutates the tab LIST, so it shares TabsListLock
|
||||
// with the worker add/remove and the refilter snapshot. Re-entrant: the
|
||||
// one worker caller (HandleTell) already holds it; framework callers take
|
||||
// it here. SavePluginConfig runs inside (short, in-memory) — the documented fallback
|
||||
|
||||
Reference in New Issue
Block a user