A Minecraft mod that lets you color-code chests and search across all of them at once -- point a search at an item and get walked straight to the chest that has it.
demo mp4 here
Download the latest release here. Grab the .jar file and drop it into your mods folder.
- Install Fabric Loader for
26.2 - Get Fabric API and your downloaded
.jarfile and drop them into yourmodsfolder. - Launch, hit
CTRL+SHIFT+RIGHT CLICK, color your chest and start tracking! - Open the search UI with
RSHIFT, type an item name and matching chest light up with a floating item above and particles around the chest.
- Color-code chests -- hit the keybind to open a color picker and tint a chest any RGB color you want. The color is applied to the chest's render state, so it shows up in-world without replacing any textures.
- Search across every colored chest at once -- type an item name and the server checks EVERY tracked chest's contents for you. You can search for several items at once; each match is marked with the specific item that was found.
- Visual guidance -- a floating item marker hovers over each matching chest with subtle
end_rodparticles around it. - Audio feedback -- a distinct sound plays for a hit vs. a miss, so you know when a search resolved without needing to look away from what you're doing.
- Tracked chests list -- see every chest you've colored from one screen, and untrack any of them without breaking the block.
- Per-world color persistence -- colors are saved locally per world/server and reload automatically next time you join.
- Server-side index persistence -- the set of colored chests is stored in the world's
SavedData, so the search index survives server restarts.
| Key | Action |
|---|---|
CTRL + SHIFT + RIGHT CLICK a chest |
Open the color picker for that chest |
RIGHT SHIFT |
Open the item search screen |
ONLY OPENING THE ITEM SEARCH SCREEN is rebindable from Minecraft's Controls > Keybinds screen (registered as [index] Open Search screen).
First get Java 25 (or a version matching your target Minecraft version, this codebase is 26.2 only).
- Clone the repository:
git clone https://github.com/mschiller890/index.git
- Build using Gradle
cd index ./gradlew build - Built jar lands in
build/libs/. - To test in a dev environment:
./gradlew runClient
The mod is split into a common (server) side and a client side, following Fabric's split-environment source set convention. The server owns the source of truth for which chests are tracked and what they contain; the client owns the colors and the UI.
src/main/java/com/mschiller890/index/ server / common
Index.java entrypoint, network handlers, tick loop
ChestItemTracker.java in-memory Item -> count index per chest
ColoredChestPositions.java SavedData: which chests are colored
ChestSearchTracker.java per-player "active search" state
ChestSearchMarkers.java per-player floating ItemEntity markers
ChestSearchParticles.java end_rod particle bursts
mixin/ BlockEntity / Level injections
network/ C2S payloads (search, set color)
src/client/java/com/mschiller890/index/client/ client only
IndexClient.java keybinds, color-picker trigger
ChestColorPersistence.java load/save colors on join/leave
helpers/ChestColorManager.java in-memory BlockPos -> ARGB map
helpers/ChestColorStorage.java NBT on disk per world id
extension/ChestRenderStateExtension.java Mixin interface on ChestRenderState
mixin/ ChestRenderer / removal injections
screens/ color picker, search, tracked chests UI
Only chests you've explicitly colored get indexed -- the mod hooks BlockEntity#setChanged() and re-scans a chest's contents into an in-memory Item -> count map whenever a tracked chest changes, rather than polling every container in the world on a timer. The set of colored positions is persisted server-side via Minecraft's SavedData system (ColoredChestPositions), so the index survives restarts. On level load, every previously-colored chest is re-scanned into the index at once.
The index itself (ChestItemTracker) is a nested map keyed by ServerLevel -> BlockPos -> Item -> count. A search is then just a lookup: iterate the level's tracked chests and collect every position whose item map contains the queried item with a count greater than zero. No world scanning, no chunk loading, no container opening.
Both a real chest break and a routine chunk unload (walking away, disconnecting, saving and quitting) call BlockEntity#setRemoved() -- Minecraft doesn't distinguish them at that hook. Untracking on every setRemoved() call meant chest colors and search state silently vanished on world reload, not just on actual destruction.
The fix uses Level#isLoaded(pos) as a guard: it's a cheap, non-blocking check, unlike getBlockEntity()/getChunk(), which can force a chunk to load and -- in earlier versions of this fix -- froze the game during "Saving world" by triggering exactly that kind of forced reload across thousands of unloading chunks at once. Only when the chunk is still loaded at the time setRemoved() fires does the mod treat it as a genuine removal.
A second mixin on Level#removeBlockEntity covers the case where a chest is broken directly (the block is removed before the block entity's setRemoved runs), so the index, search state, and markers are cleaned up either way.
A search request is a single network round trip:
- The client builds a list of item IDs (from the search UI's checked items) and sends a
SearchItemsC2SPayload. - The server resolves each ID against the registry, looks each item up in the in-memory index, and collects matching
BlockPos -> Itempairs. - The server plays a hit/miss sound (
note_block_plingfor a hit,note_block_bassfor a miss) directly to the searching player. ChestSearchMarkersspawns one lightweight, non-interactiveItemEntityper match, floating 1.2 blocks above the chest. Each marker is tagged withindex_markerplus a per-player tag (index_marker_<uuid>), so it never appears for anyone but the searcher.- A server tick task (every 10 ticks) spawns
end_rodparticles around each active match for the player who owns the search.
Markers are cleaned up the moment a match is opened (the UseBlockCallback removes that specific marker), the player disconnects (all of that player's markers are discarded), or the chest itself is destroyed (removeMarkerFromAllPlayers).
Colors live entirely on the client. ChestColorManager holds a BlockPos -> ARGB map in memory, and ChestColorStorage persists it to <game dir>/index/chest_colors/<worldId>.dat as compressed NBT. The world id is derived from the singleplayer world folder name (sp_<name>) or the multiplayer server IP (mp_<ip>), so colors are scoped per world/server and never leak between them.
The actual tinting is done with a @ModifyConstant on ChestRenderer#submit that replaces the hardcoded tint (-1, i.e. white) with the stored color for that chest's position. To get the position into the render state, a small mixin (ChestRenderStateMixin) attaches a BlockPos field to ChestRenderState via the ChestRenderStateExtension interface, which is populated in extractRenderState and read back in submit. Ender chests are intentionally skipped so they keep their vanilla look.
When a chest is colored or uncolored, the client also sends a SetChestColorC2SPayload so the server can add/remove the position from ColoredChestPositions and start/stop indexing it. The client mirrors the same "is this a real removal?" guard (isLoaded) before clearing its local color, so colors survive chunk unloads.
Two custom payloads, both client-to-server:
| Payload | Purpose |
|---|---|
SetChestColorC2SPayload(pos, colored) |
Add/remove a chest from the tracked set |
SearchItemsC2SPayload(itemIds) |
Request a search across the index for a list of items |
All server-side handling is dispatched onto the server thread via context.server().execute(...), so the index and SavedData are never touched off-thread.
Built on Fabric and Fabric API