Skip to content

Key the explosion scan by packed block position - #1021

Open
tricrotism wants to merge 1 commit into
PlayPro:masterfrom
tricrotism:for-upstream/explosion-packed-keys
Open

tricrotism wants to merge 1 commit into
PlayPro:masterfrom
tricrotism:for-upstream/explosion-packed-keys

Conversation

@tricrotism

Copy link
Copy Markdown

Summary

Every logged explosion builds and hashes a pile of Location objects on the region thread to find attached blocks next to the blast. Keying the scan by a packed long instead halves CoreProtect's cost per explosion on Paper in my measurements, with the same blocks logged.

The problem

BlockExplodeListener.processBlockExplode (listener/block/BlockExplodeListener.java:34-88) runs for every explosion that CoreProtect logs:

  • it puts every exploded block into a HashMap<Location, Block>, which clones a Location per block;
  • it copies that whole map before scanning it;
  • for each block it allocates five neighbour Locations (+x, -x, +z, -z, +y) and looks each one up. Location.hashCode and equals hash and compare three doubles, yaw, pitch and the world on every lookup;
  • a bisected neighbour (doors, tall plants) clones one more Location, and the world's min and max height are re-read inside the loop.

A TNT or creeper blast touches dozens of blocks, so this is hundreds of short-lived objects and double hashes per explosion, all on the thread that owns the blast.

The fix

  • The map is keyed by the block position packed into a long, using the same bit layout Minecraft uses for BlockPos (26 bits x, 26 bits z, 12 bits y).
  • The five neighbours come from a static offset table and world.getBlockAt(int, int, int), with no Location built.
  • Only the map's values are copied before the scan, and the height bounds are read once.

The set of blocks logged is the same. Their order within one explosion follows the new map's iteration order. Upstream's HashMap<Location, Block> order was already arbitrary.

Behaviour change

None.

Risk

Low. One file, no signature change. The packing is exact for every coordinate a world can hold (x and z within ±33,554,432, y within the 4,096 block range).

Testing

Build: mvn package passes.

Performance, PathLoad explosions mode on Paper 26.2, 2 GB heap, autosave off: 100 explosions per second, 60 s warmup, then the time of each createExplosion call over a 60 s window. Three runs of each jar, alternating. With no CoreProtect installed a call costs 151.4 µs.

Jar Run 1 Run 2 Run 3 Mean CoreProtect's share
upstream 3af1079 168.1 µs 170.2 µs 165.9 µs 168.1 µs ~16.7 µs
this branch 154.8 µs 159.6 µs 163.6 µs 159.3 µs ~7.9 µs

The same runs on Folia 1.21.11 varied by up to 17 µs between runs of one jar and did not separate either way.

Row parity: a 47-step scenario plugin (which includes an explosion, then rollback and restore) on Paper 26.2 and Folia 1.21.11 with SQLite, compared order-insensitively against upstream run the same way. The explosion rows are identical. The only other differences are the plant that bone meal happens to grow (random on every run) and one far-basin water row whose settling window lands either side of the boundary depending on run pacing. No new errors.

The explosion scan cloned a Location for every block and built five more per block to find attached neighbours, hashing each one on the region thread. It now keys the map by a packed long position and walks the neighbours from an offset table. The same blocks are logged.
@netlify

netlify Bot commented Sep 23, 2026

Copy link
Copy Markdown

❌ Deploy Preview for coreprotect failed. Why did it fail? →

Name Link
🔨 Latest commit c6520f4
🔍 Latest deploy log https://app.netlify.com/projects/coreprotect/deploys/6ab3f008f9a58f0008afabc6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant