Optimize sorting tree container lookup#2670
Conversation
| @@ -1,31 +0,0 @@ | |||
| package twilightforest.util; | |||
There was a problem hiding this comment.
It was only used by the Sorting Tree (and its dedicated test). This PR no longer needs it because, while iterating loaded block entities, we already have the BlockEntity and BlockState and can query the capability directly. Leaving it would therefore be dead code.
Its map also lived for the lifetime of the block singleton without eviction, was keyed only by position and direction (not by ServerLevel), and the previous cube iterator could supply mutable BlockPos instances. Retaining it would preserve the lifetime and cross-level keying issues this change is intended to remove. The test was removed along with the now-unused helper.
There was a problem hiding this comment.
NeoForge devs strongly recommend to use BlockCapabilityCache whenever possible.
There was a problem hiding this comment.
There was a problem hiding this comment.
Thanks for pointing this out and for linking the NeoForge guidance. I reworked this in 7c0c4d8 to keep using BlockCapabilityCache for each loaded block entity and side.
To avoid the lifetime and cross-level issues in the previous helper, the caches are now partitioned by ServerLevel, use weak block-entity keys within each level, and explicitly clear the complete per-level table on server-side LevelEvent.Unload.
I added tests for reuse, directional separation, identical positions in different levels, and selective cleanup. I also exercised the exact cache implementation on a NeoForge dedicated server: 500,000 repeated lookups kept the table sizes stable, and shutdown cleanup went from 2 level tables to 1 and then 0.
Could you please take another look and let me know whether this now matches the recommended pattern?
There was a problem hiding this comment.
Not sure if a single event needs its own class, would possibly fit better into MiscEvents.
There was a problem hiding this comment.
That's a reasonable suggestion. MiscEvents is a better fit for a single stateless handler. I moved the unload listener there and removed the dedicated event class in 73e506b. Thanks!
|
Just an FYI, I don't want to merge a change like this until the initial 1:1 parity port is complete so this is going to be placed on the back burner but it is being considered regardless. Though I personally feel some rework needs to be done to not have the field inside the block itself. I would love for it to be a block entity instead but that also has some drawbacks we're not willing to make, for example: allowing pistons to push it around. But that's out of scope for this PR and something we'd revisit later. |
What changed
BlockCapabilityCachewhile the target block entity is loaded.ServerLevelidentity, use weak block-entity keys within each level, and clear the complete per-level table on server-sideLevelEvent.Unload.Why
The sorting tree runs once per second. At the default range of 16, the previous implementation checked 35,937 block positions per active core before it could begin sorting. The loaded-chunk lookup does not load or generate chunks and scales with loaded chunks and block entities rather than the volume of the search cube.
The follow-up keeps NeoForge's recommended
BlockCapabilityCachefor repeated queries without restoring the old lifetime and cross-level keying problems. Caches are separated by level, ordinary block/chunk invalidation releases cached handlers, and level unload explicitly removes the remaining per-level object graph.This is the forward port of #2669 to the current
latest/ 26.1 branch.Validation
-Xlintflags and-Werror: passed for the changed production code and tests against the real Minecraft 26.1.1 / NeoForge 26.1.1.6-beta classpath.git diff --check: passed.The full upstream
latestbuild is not currently a usable signal: the base branch has unrelated unfinished 26.1 port compilation errors, and its Azure pipeline still selects JDK 21 while the Gradle toolchain requires Java 25. The isolated checks above avoid hiding that baseline condition while still compiling and exercising the changed code.