Fabric client mod: visual culling of stacked entities for FPS
  • Java 93.5%
  • Shell 6.5%
Find a file
2026-07-15 01:38:37 +02:00
docs Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
gradle/wrapper Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
scripts Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
src/main Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
tests Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
.gitignore Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
build.gradle Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
gradle.properties Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
gradlew Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
gradlew.bat Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
HANDOFF.md Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
LICENSE Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
README.md Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00
settings.gradle Initial snapshot: Fabric client mod for visual culling of stacked entities 2026-07-15 01:38:37 +02:00

EkaiiEntityCull

Client-side visual culling of stacked entities for Minecraft 26.1.2 (Fabric).

When the same EntityType piles up in a small spatial cell (boats, item entities, minecarts, mobs), the renderer is told to early-return for everything past the configured threshold. The server is never touched — this is purely a visual trick for the client.

Initial use-case: the ekaii creaclone stacked-propulsion machines (500+ boats inside a single block) — render only 10 so your FPS doesn't collapse, while gameplay behaviour stays untouched.

Behaviour

  • Per client tick: every entity in level.entitiesForRendering() is sorted into buckets keyed by (EntityType, spatial cell). Within each bucket, the first max_per_bucket entities (by stable entity-id) are kept; the rest are flagged and skipped at render time.
  • The render mixin (EntityRenderDispatcher.shouldRender at HEAD) cancels rendering for non-allowed entities — this hides their model, name tag, bounding box, and any per-entity particle/effect drawing the dispatcher would have done.
  • Never culled: Player instances, and entities with a custom name tag (toggle via preserve_named_entities).

Settings UI

Three ways to configure, pick whichever is on your desktop:

  1. Mod Menu (recommended): install Mod Menu + Cloth Config, open the mods list (ESC → "Mods" in vanilla, or Mod Menu's icon on the main menu), click the gear icon next to "EkaiiEntityCull" → vanilla-styled settings screen with general toggles and a per-entity-type override tab.

  2. Sodium (if installed): the mod auto-registers a tab inside Sodium's "Video Settings" screen, complete with Sodium-style sliders, tooltips, and impact badges. Per-type overrides are reachable via an "External" button from there.

  3. Config file + command (headless / mod-pack):

    • Edit ~/Library/Application Support/EkaiiLauncher/synced/config/ekaii-entity-cull.json5 (or config/ekaii-entity-cull.json5 in the run dir).
    • /ekaiicull reload | set <key> <value> | (no args) in chat.

The mod works without any of those three mods installed. The toggle key (H), the slash commands, and the F3 overlay are always available regardless.

Controls

  • H — toggle on/off (rebindable in vanilla Controls menu), shows an actionbar message.
  • /ekaiicull reload — re-read the config file.
  • /ekaiicull set <key> <value> — mutate + persist a config field at runtime. Keys: enabled, max_per_bucket, bucket_size_blocks, preserve_named, force_visibility, cull_use_y_in_key.
  • /ekaiicull debugf3 — toggle F3 injection event capture (v0.2.4). First invocation arms the ring buffer; second invocation prints a summary
    • the last 20 events into chat. Use this to verify the duplicate-line fix without restarting the client.
  • /ekaiicull (no args) — print current state in chat.

Config

~/Library/Application Support/EkaiiLauncher/synced/config/ekaii-entity-cull.json5

Defaults:

// EkaiiEntityCull config — JSON5 (line comments + trailing commas OK).
// Reload at runtime: /ekaiicull reload     Toggle: H key (rebind in Controls).
{
  "enabled": true,
  "maxPerBucket": 10,
  "bucketSizeBlocks": 1,
  "perTypeOverride": {},
  "preserveNamedEntities": true,
  "forceVisibility": false,
  "cullUseYInKey": false
}

perTypeOverride keys are EntityType registry IDs (e.g. "minecraft:boat": 3). The path-only form ("boat": 3) also works. Set a value to 0 in the screen to remove the override (use the global maxPerBucket).

forceVisibility (default false, changed in v0.2.3)

Controls whether the entities we KEEP are FORCED to render, overriding downstream cullers like tr7zw EntityCulling and moreculling on the same shouldRender hook.

  • false (default) — we only cull the entities we want hidden; decisions on the rest are left to vanilla logic and any other mod that also hooks shouldRender. Critically, this lets tr7zw EntityCulling's tick-skip optimization apply to our kept entities — that's tr7zw's main perf win (~1% low FPS gain). Recommended for almost everyone.

  • true — kept entities are forced visible no matter what. Use this ONLY if you have stacked entities inside an enclosed machine (e.g. boats inside a sealed propulsion box) AND they all turn fully invisible because tr7zw's occlusion ray-trace marks them as hidden. The trade-off is that forcing visibility short-circuits tr7zw before its tick-skip runs, so you lose the 1% low FPS gain.

How to flip if your stacked-machine boats disappear:

/ekaiicull set force_visibility true

Or open Mod Menu → EkaiiEntityCull → gear icon → toggle "Force visibility". Or open Sodium settings → EkaiiEntityCull tab → toggle "Force visibility".

(Upgrading from v0.2.2? Your saved config still has whatever value you had before. To take the new default, run /ekaiicull set force_visibility false or delete the line from the JSON file and /ekaiicull reload.)

cullUseYInKey (default false, since v0.2.2)

false = column bucket (XZ only) — vertically-stacked entities cluster into one bucket regardless of Y. true = cubic bucket (XYZ) — each Y layer is its own bucket. Column is the right key for the boat-propulsion use case (the motivating workload).

F3 debug

A single line in the F3 right column shows:

[EntityCull] ON | kept 10 / culled 44 / total 54  N=10  bk=1(col)  maxB=54  fv=n
  Boat: 10/54
  Item: 8/103
  • kept / culled / total = decision breakdown.
  • N = max_per_bucket.
  • bk=1(col) = bucket size in blocks, mode (col)umn or (xyz) cubic.
  • maxB = biggest single bucket population this tick (diagnoses scatter).
  • fv=Y/n = forceVisibility on/off.

Below: top-3 types by visual difference (most aggressively culled). The block only appears when F3 is actually visible — it never leaks onto the main menu or regular GUI.

v0.2.4 duplicate-line fix: v0.2.1, v0.2.2, and v0.2.3 all shipped with a still-visible "duplicate [EntityCull] line" bug. v0.2.3's thread-local frame gate was reset by each F3 sub-panel's extractRenderState call (MC 26's split-overlay architecture, amplified by Lithium / Sodium / tr7zw on the same hook), so the gate kept re-arming. v0.2.4 walks the F3 line list itself before appending and bails out if a line already starts with [EntityCull] — call-path-independent and bulletproof regardless of how many times the inject fires per frame.

Debugging the F3 injection in-game: run /ekaiicull debugf3 to start capturing RESET / EXTRACT-APPEND / EXTRACT-skip events into a ring buffer, open F3 for a few seconds, then run /ekaiicull debugf3 again to print a summary + the last 20 events into chat. A healthy session shows APPEND >= 1 and SKIP(scan) + SKIP(gate) matching whatever extra calls the F3 pipeline made — no duplicates either way.

Screenshots

Placeholders — to be added once v0.2.0 is in-game tested:

  • docs/img/modmenu-entry.png — Mod Menu gear button
  • docs/img/cloth-screen-general.png — General settings tab
  • docs/img/cloth-screen-per-type.png — Per-type overrides
  • docs/img/sodium-tab.png — Sodium settings tab
  • docs/img/f3-overlay.png — F3 right column with EntityCull stats

Build

./gradlew build
# → build/libs/ekaii-entity-cull-0.2.4.jar

Install

cp build/libs/ekaii-entity-cull-0.2.4.jar \
   "$HOME/Library/Application Support/EkaiiLauncher/instances/26.1.2-1/.minecraft/mods/"

Stack

  • Minecraft 26.1.2
  • Fabric Loader 0.19.2, Fabric API 0.149.1+26.1.2
  • Cloth Config 26.1.154, Mod Menu 18.0.0-beta.1, Sodium 0.8.12+mc26.1.2 (all three optional at runtime)
  • Cached mojmap (Mojang no longer publishes 26.1.2 mappings — see HANDOFF.md)
  • Loom 1.15.5, Gradle 9.4.0, Java 25 (Temurin)

License

MIT. See LICENSE.

Acknowledgements

The settings-UI patterns are inspired by tr7zw's EntityCulling (Mod Menu + Cloth-like screen + Sodium-coexistence) — see docs/tr7zw-entityculling-analysis.md for the full pattern study and the "what we borrowed vs. what we didn't" decision log. None of tr7zw's code is bundled; our implementation is clean-room.