Conversation
Five defects, none of which required new behaviour to surface: - v7 job migration aliased its own array. `bool[] JobTemp = doodle.JobsBool` is a reference, so the ascending shift read slots it had already written and smeared old index 14 (BRD) across MCH, DNC, BLM, SMN and RDM. Now shifts descending in place. Configs already stamped Version 7 are past the point of repair; this only protects users still on a pre-7 config. - The migration also blind-indexed [20] and [22], throwing on any stored array shorter than 21. Guarded on the expected length, and the v4 block is bounds- and null-checked the same way. - Dispose removed OpenConfigUi while the constructor subscribed OpenMainUi, so the live handler leaked across reloads. The stale commented-out OpenConfigUi line goes with it; the gear icon stays deliberately inert. - The first-run welcome window called ImGui.Begin with no matching End. - Clipboard import trusted foreign JSON: a null document threw inside AddRange, and a pre-7.0 export carries 21 job flags where everything downstream assumes 23, overrunning _doodleJobs and _doodleJobsUint on a later frame — outside the try, so the catch never saw it. Imports are now null-filtered and normalised, as is the config load path. The editor drew every doodle at the running sum of all preceding offsets, because dotPosX/dotPosY were declared outside the loop and only ever accumulated. The anchor is now immutable and each doodle offsets a local copy, which also un-skews the line-endpoint hit tests. Not compiled: no .NET SDK or Dalamud assemblies were available. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cleanup: - Remove usings that nothing references: the stray JSType import, the unused Num and Condition aliases, Lumina.Excel.Sheets, System.Linq, Dalamud.Interface, Dalamud.Interface.Windowing, and Dalamud.Game.Gui (IGameGui is declared in Dalamud.Plugin.Services). Dalamud.Game and Dalamud.Game.ClientState are left alone — grep says dead, but that cannot be confirmed without compiling. - Remove IdToJob and the JobIds enum. Neither has a caller; the live job mapping is _doodleJobsUint. Drawing.Job stays, since it is serialised config the v4 migration still reads. - Remove the target coordinate and atan readouts left in the Cone tab. - Remove the `vector` and `job` locals, both overwritten a line later. - Drop AssemblyVersion from the manifest. DalamudPackager fills it from the assembly and overwrites whatever is there, which is how it drifted to 3.3.3.0 against the csproj's 3.3.4.0 in the first place. - Hoist the Occupied38 cutscene check out of the per-doodle loop. Saving: The config was written every 100 frames while the window was open, regardless of whether anything changed, and edits in the last <100 frames were lost if the game exited. Add Doodle, Delete, reorder and the editor's endpoint drag never saved at all. Each widget now reports its own change through its return value, and a single flush runs once per frame gated on IsAnyItemActive, so a drag coalesces into one write on release. The editor marks dirty when an endpoint is dropped rather than on every frame it is held, since a canvas drag leaves no ImGui item active and would otherwise write every frame. The reorder branch's early return is now an if/else so the flush stays reachable on that path. Not compiled: no .NET SDK or Dalamud assemblies were available. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes several latent defects, removes dead code, and replaces the frame-counter autosave with save-on-change. No intended change to the shapes users see on screen.
These changes have not been compiled. No .NET SDK or Dalamud assemblies were available on the machine they were written on, so everything below was verified by static inspection only. Please build before merging.
Correctness
Unbalanced ImGui window stack. The first-run welcome window called
ImGui.Beginwith no matchingImGui.End.Leaked event handler. The constructor subscribes
UiBuilder.OpenMainUi += ConfigWindow, butDisposeunsubscribedOpenConfigUi. The live handler was never detached, so a plugin reload left the old delegate attached.Disposenow removesOpenMainUi. The gear-icon binding is deliberately left as-is, per 1dee8fc.The v7 migration corrupted job selections.
bool[] JobTemp = doodle.JobsBool;aliases the array rather than copying it, and the shift loop ran ascending, so each iteration read the value the previous one had just overwritten. Slots 15 through 20 (BRD, MCH, DNC, BLM, SMN, RDM) all ended up holding the old BRD value. The loop now runs descending, which makes the in-place shift correct, and the redundant alias is gone. The block is also guarded to only run on arrays of exactly the pre-7.0 length of 21, since it blind-indexes[20]and[22].Note this does not repair configs that already migrated. They have
Version == 7stored, so the block will not re-run, and the pre-shift state is unrecoverable. Affected users have to re-tick jobs 15 through 20 by hand.Editor offsets accumulated across doodles. The player-center pixel was computed once outside the
foreachover_doodleBagand then+='d inside it by the ring, dot, and dashed-ring branches, without ever being reset. Doodle N rendered at the sum of every preceding doodle's offset, and the line-endpoint hit tests read the same drifting value, so dragging was mis-aimed once any offset doodle preceded a line. The anchor is now immutable and each iteration derives its own draw position from it. The variables were renamed (anchorX/anchorYfor the fixed origin,drawX/drawYfor the per-doodle position) so that any site missed during the split fails to build rather than silently drawing at the wrong pixel.Clipboard import trusted its input.
DeserializeObject<List<Drawing>>returns null for the input"null", andAddRange(null)then threw. ImportedJobsBoolarrays also carried the exporter's length, and a pre-7.0 export has 21 entries where everything downstream assumes 23, which overran_doodleJobsand_doodleJobsUinton a later frame, outside thetry. Import now null-guards, drops null entries, and normalizes every imported drawing.Bounds in the per-frame path.
CheckJobwalkedjobListwhile indexing_doodleJobsUintunbounded. It is now an indexed loop bounded by the shorter of the two. The job checkbox grid had the same shape and got the same treatment. A newNormalizeJobshelper pads or truncatesJobsBoolto the current job-table length, and runs both on config load and on import.Cleanup
AssemblyVersionfromPixelPerfect.json. DalamudPackager fills it from the assembly and overwrites whatever is in the file, so it was a second source of truth that had already drifted from the csproj.IdToJobmethod andJobIdsenum, dead locals in the doodle tab, and the debug readout that printed raw target coordinates into the Cone tab.Occupied38condition check out of the per-doodle loop into an early return.Save on change
The config previously saved every 100 frames while the window was open, whether or not anything had changed, and edits made in the last stretch before exit were lost. Add, delete, and reorder never saved at all.
Each mutating widget now sets a dirty flag from its own return value, and the config is written once per frame when that flag is set and no widget is active. Gating on
ImGui.IsAnyItemActive()coalesces a slider drag into a single write on release. The editor's endpoint drag sets the flag on mouse release rather than per frame, for the same reason. The explicit save on the Close button is unchanged.Testing
Static checks only: ImGui
Begin/End,PushStyleVar/PopStyleVar,PushItemWidth/PopItemWidth,PushStyleColor/PopStyleColor,Columns, andTreeNode/TreePopall pair on every path; everyUiBuildersubscription has a matching unsubscription; the migration shift was simulated against a known bit pattern in both directions; every removed symbol has zero remaining references.Worth exercising in game before merge: the welcome window on a fresh config, two offset doodles rendering independently in the editor, line-endpoint dragging landing under the cursor, importing a pre-7.0 export string, upgrading a pre-7 config with jobs ticked, reloading via
/xlpluginswithout a duplicate overlay, and edits persisting after closing the config without pressing Close.