The original v1.0.0 changelog only documented the rebrand. After the
CodeRabbit pass added 9 follow-up fix commits (3 critical bugs plus
21 major findings, grouped into safety / crash-class / correctness /
threading / resource / performance categories), the changelog needs
to reflect what users are actually receiving.
yaml + repo.json synchronized.
The DB export loop called filteredHistory.Count twice per 5000-message
batch — once for the progress fraction, once for the status text.
filteredHistory is built lazily by Filter() and re-enumerates on every
.Count access, so on a 2M-message history each batch was paying for
two full passes through the IEnumerable. Materializing the count once
at the top reduces the export to a single O(N) traversal as intended.
The RunOnTick + delayTicks pacing is intentional (keeps SeString
encoding on the framework thread and rate-limits the export to avoid
laggy frames during long exports), so the rest of the loop stays put.
Two pre-existing build/security defects flagged by CodeRabbit:
- HellionChat.csproj sets RestorePackagesWithLockFile=true so dotnet
restore honors the committed packages.lock.json. Floating version
ranges in the lockfile previously could drift between machines or
CI runs, producing builds with subtly different transitive
dependencies
- HellionChat.csproj pins SQLitePCLRaw.lib.e_sqlite3 to 3.50.3 to
override the older 2.1.11 native build that
Microsoft.Data.Sqlite 10.0.7 transitively pulls in. Ships SQLite
3.50.3 which contains the fixes for CVE-2025-6965 (memory
corruption from aggregate-term overflow) and CVE-2025-7709. The
managed Microsoft.Data.Sqlite wrapper stays on 10.0.7 — only the
native binary is bumped, no API breakage. Verified via the NuGet
spec: "the first three numbers in the version number of this
package indicate the version of SQLite that was used to build it"
- EmoteCache.cs replaces the per-call "new HttpClient()" with the
existing static Client field. The static instance already exists
for two other endpoints in the same file and reuses connection
pooling; the third call site was a stray that leaked a socket
on every emote download
- FontManager.cs wraps both the HttpClient and the HttpResponseMessage
in using-blocks, replaces the .Result/AggregateException sandwich
with GetAwaiter().GetResult() for clean exception propagation, and
adds EnsureSuccessStatusCode so failed downloads don't silently
produce a zero-byte font file. Full async refactor of the FontManager
constructor is tracked separately
- Util/AutoTranslate.cs introduces a single EntriesLock object and
serializes every read and write of the static Entries dictionary
and ValidEntries hash set behind it. PreloadCache spawns a worker
thread that fills both while the main thread reads them via the
Matching / ReplaceWithPayload / StartsWithCommand entry points;
without the lock the underlying collection access was undefined.
AllEntries() splits into a thin lock wrapper plus a private
BuildEntriesLocked() helper that runs under the lock
- Ui/SettingsTabs/Privacy.cs bounds the .Wait() on the framework
refresh after a manual retention sweep and after the privacy
cleanup. A hung framework tick previously could deadlock the
background worker thread. Five-second timeout, log on miss
- Util/SearchSelector.cs ImRaii.PushId(id) collapsed every row in the
filtered list to the same ImGui ID, leaving the ID stack ambiguous
for click resolution. Mix the row index into the pushed id so every
Selectable has a distinct ImGui identifier
- Ui/SettingsTabs/Chat.cs blocked-emote add-button never opened the
selector popup because SearchSelector.SelectorPopup is wrapped in
ImRaii.ContextPopupItem (right-click semantics). Detect the
IsItemClicked() event after the button and call ImGui.OpenPopup
explicitly so left-click opens the picker too
- Ui/Debugger.cs DebuggerWindow now declares IDisposable so the
existing Dispose() method participates in disposal patterns
(using-blocks, container cleanup). Previously the method existed
but the type didn't advertise it, so callers had no way to invoke
it correctly and the command-handler subscription leaked
- Resources/Language.zh-Hans.resx Webinterface_Start_Success
contained "网页界面已停止。" (web interface stopped) for what is
semantically the start-success message; corrected to
"网页界面已启动。" (web interface started). String is unused in
the Hellion fork (webinterface removed) but remains in the
resource bundle for upstream compatibility
Two pre-existing upstream defects fixed in v1.0.0:
- Ui/Popout.cs PopOutDocked[Idx] now bounds-checks Idx against
ChatLogWindow.PopOutDocked.Count before reading or writing. A
popout instance can outlive a list resize when AddPopOutsToDraw()
rebuilds the docked-state list while a draw frame is in flight,
which previously produced an out-of-range crash on tab drop
- Ui/SettingsTabs/Tabs.cs guards against an empty worlds list before
indexing worlds[selectedWorld]. Empty lists can occur briefly when
switching characters or before the datacenter sheet finishes
loading — the previous code would crash with an
ArgumentOutOfRangeException
Four pre-existing upstream defects fixed in v1.0.0:
- Util/GlobalParametersCache.cs GetValue captures Cache into a local
before the bounds check, so the check and the indexed read operate
on the same array reference even when Refresh reassigns Cache from
the main thread between the two operations
- Util/IconUtil.cs binary search bounds: hi initialized to
entries.Length-1 (was Length), and reset on redirect-restart;
added entries.Length==0 short-circuit to prevent indexing into
empty arrays
- Sheets.cs WorldsOnDatacenter compared Region.RowId, which groups
by region instead of datacenter — now compares DataCenter.RowId
directly so the result actually reflects same-DC worlds
- Message.cs back-reference loop iterates the processed Sender/Content
properties rather than the raw constructor parameters, so chunks
added or replaced by CheckMessageContent also get Message set
Three pre-existing upstream defects flagged by CodeRabbit, fixed in the
v1.0.0 standalone cut where we own the codebase:
- Ipc/ExtraChat.cs Dispose now unsubscribes all three IPC subscriptions
(OverrideChannelGate, ChannelCommandColoursGate, ChannelNamesGate)
instead of only the first; previously the latter two leaked their
subscriptions on every plugin reload
- GameFunctions/Types/TellTarget.cs FromTarget guards against a zero
IPlayerCharacter.Address before dereferencing the unsafe Character*
cast; previously a missing/destroyed target object would crash the
game on /tell construction
- GameFunctions/GameFunctions.cs ResolveTextCommandPlaceholderDetour
null-checks the Hook reference before calling .Original instead of
using the null-forgiving operator; defensive guard for teardown races
UnregisterGate is registered via RegisterAction(Unregister) on
construction (Unregister returns void), but Dispose was calling
UnregisterFunc() instead of UnregisterAction(). The mismatched
unregister call leaks the action subscription on plugin reload —
subsequent Dispose/Init cycles would accumulate orphan handlers
in the Dalamud IPC layer.
Pre-existing upstream issue (CodeRabbit critical finding); fixed in
v1.0.0 standalone cut where we own the codebase.
Equals(object?) was delegating to GetHashCode() comparison, which is
the textbook hash-collision anti-pattern: two distinct ChatCode values
could in principle share a hash and be wrongly reported as equal. The
current GetHashCode implementation packs Type/Source/Target into 24
bits and happens to be collision-free, but the contract is fragile —
any future change to GetHashCode silently breaks Equals.
Replaced with direct field-by-field comparison of Type, Source, Target.
GetHashCode is left unchanged so dictionary/HashSet behavior stays
identical.
Pre-existing upstream issue (CodeRabbit critical finding); fixed in
v1.0.0 standalone cut where we own the codebase.
The previous implementation nested a ValueInRange helper that used
strict inequalities at both ends. That dropped identical rectangles
and shared-edge cases as false negatives — two rectangles with
a.X == b.X would miss the overlap on the X axis even when they
clearly overlapped.
Replaced with the standard AABB test: rectangles overlap iff they
overlap on both axes (a.Min < b.Max && a.Max > b.Min per axis).
Pre-existing upstream issue (CodeRabbit critical finding); fixed in
v1.0.0 standalone cut where we own the codebase.