Alias the wildcard ingredients view when there is only one channel - #222
Conversation
Coverage Report for CI Build 34141482526Coverage decreased (-0.08%) to 20.491%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions317 previously-covered lines in 2 files lost coverage.
Coverage Stats
💛 - Coveralls |
The client tab keeps one collapsed ingredients collection per channel plus one for the wildcard channel, which holds the sum over all channels. Every change event was applied to both, so on a single-channel network the same collection was built and updated twice, once for the channel and once for its identical wildcard copy. The wildcard channel is what a terminal shows by default, so neither copy could simply be dropped. With exactly one channel the wildcard view now aliases that channel's view rather than duplicating it. The alias is broken, and an independent copy taken, as soon as a second channel appears, so multi-channel behaviour is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mxjin31W1Lmq5XK1CCe84v
8a67c6f to
1157918
Compare
|
A
Nothing was re-measured on 1.21. The terminal open harness exists only on the 26.1 measurement branch and its instrumentation spans seven files that diverge substantially between the branches, so porting it would have been hours of work to re-derive numbers for an identical file. #223 says that plainly rather than implying its figures came from 1.21. This PR stays open. Merge whichever branch suits your upmerge direction. Generated by Claude Code |
Two problems with how the alias was handled. isWildcardViewAliased was a query that mutated: it broke the alias and replaced the wildcard entry of ingredientsUnsortedViews as a side effect of being asked a question, and getRawUnfilteredIngredientsView established the alias as a side effect of a lookup. All of that now happens in updateWildcardViewAlias, called where the set of known channels changes. The query only reports state, and the view getter only looks up a map. The wildcard recursion in onChange also ran before the diff was applied. That was harmless while the wildcard held its own collection, because the recursion applied its own copy of the diff. With the collection shared it was not: the recursion reset the wildcard filtered view and then rebuilt it, from the shared collection, before the diff reached it. The terminal kept showing the contents from before the change. The diff is now applied before the recursion. Found by opening a terminal on a real network and adding an item to a chest while it was open: the total updated but the item list did not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mxjin31W1Lmq5XK1CCe84v
TerminalStorageTabIngredientComponentClientkeeps one collapsed ingredients collection per channel plus one for the wildcard channel, which holds the sum over all channels.onChangerecurses into the wildcard channel, so every change event is applied to both. On a single-channel network those two collections always hold exactly the same thing, so the same collection is built and updated twice. Neither copy can simply be dropped, because the wildcard channel is what a terminal shows by default.With exactly one channel the wildcard view is now the very same object as that channel's view, and the diff is applied to it once instead of twice. The alias is broken, and an independent copy taken, as soon as a second channel appears, so multi-channel behaviour is unchanged.
Establishing and breaking the alias happens in one place,
updateWildcardViewAlias, called from the two spots where the set of known channels changes.isWildcardViewAliasedonly reports state andgetRawUnfilteredIngredientsViewonly looks up the map, so neither mutates as a side effect of being called.A display bug this uncovered
The first version of this change was wrong, and in-game testing caught it.
onChangerecursed into the wildcard channel before applying the diff to the channel's own collection. That was harmless while the wildcard held its own separate collection, because the recursion applied its own copy of the diff. With the collection shared it was not: the recursion reset the wildcard filtered view and rebuilt it from the shared collection, which had not received the change yet. Opening a terminal and then adding an item to a chest updated the total quantity but left the item list showing the contents from before the change.The diff is now applied before the recursion.
Behaviour
Two rounds of verification, both against a live dev client by reaching into the running client and comparing the wildcard view against the per-channel views.
The synthetic networks below were run on this branch, on 26.1.
Four channels, 500 stacks each. The wildcard view stays independent and holds the sum:
One channel, 2000 stacks. The wildcard view is the channel view, and holds the ingredients once rather than twice:
Feeding a change on a second channel into that same open terminal breaks the alias correctly:
The second round, on a real Integrated Dynamics network with a player opening the terminal, was run on the 1.21 branch (#223), not here. That is where the display bug above was found. The client dev environment was already set up there and the changed regions are the same delta on both branches, so I did not repeat it on 26.1; saying so plainly rather than implying otherwise. From that round:
Before the ordering fix the same run gave
filtered(wildcard)=5againstfiltered(0)=6../gradlew buildand./gradlew runGameTestServerboth pass on this branch (35 required tests).Numbers
10 000 stacks, first open, loopback, medians of six and nine runs after a discarded warm-up. Both runs are against a CyclopsCore build carrying CyclopsMC/CyclopsCore#238, which is the change that actually made terminal opens fast; this PR is measured on top of it. Times in ms, client-side except the last row.
The duplicated work is real and it goes away: applying the incoming changes costs a third less and rebuilding the sorted view a quarter less. End to end it is not visible. Client completion moves by 2%, which is inside the run-to-run spread of these measurements, and the server difference is noise since this is a client-only change.
So this is worth taking for removing a genuine duplicate and halving the client-side memory a terminal holds on a single-channel network, not for a speed claim. I would not have opened it on the timings alone.
Context
This came out of measuring the cost of opening a storage terminal for #219. The full report, including the profiles, the scaling numbers, and a recommendation to park #219, is in
MEASUREMENT-2.mdon theclaude/measure-terminal-open-cost-lj4kn1branch. The instrumentation used to measure this is on that branch and deliberately not on this one.#223 is the same change against
master-1.21-ltsand stays open. Merge whichever suits your upmerge direction.Note for review
There is no automated test here. The class is client-only and its constructor needs a live container and GUI state, so it cannot be reached from a unit test or from
runGameTestServer, which is a dedicated server. The verification above is the substitute.No changelog entry, as requested.
Generated by Claude Code