Welcome to the Hangar Open Beta. Please report any issue you encounter on GitHub!
Avatar for xeaf

A plugin for searching items and blocks through the entire world and seeing people's inventories and enderchest

Report StolenItemAdmin?

StolenItemAdmin

An admin toolkit for tracking down where stolen/stashed items have ended up, without relying on item tracing - so it works retroactively on worlds that were never set up to trace anything. Built on top of the original RecursiveInvSee inventory viewer.

Targets Paper / Folia build 26.1.2 (and should keep working on other builds in that 26.1+ line, and reasonably far beyond it - see "Compatibility" below).

WARNING: Made with AI

I am a random dev, and you should really not trust random stuff you find on the Internet. Take a look at the source code (click the GitHub "view source" link on the side), and compile it yourself with mvn clean package (requires Java JDK 21+ and Maven installed) if you'd rather not trust a prebuilt jar.

Because offline inventory editing writes directly to a player's save file, test on a throwaway world/backup first. A .bak copy of the .dat file is made automatically right before every save as a safety net, but you're still editing raw player data outside of a running game session.

Inventory / ender chest viewing

  • /invsee <player> - view/edit a player's inventory, online or offline
  • /endersee <player> - view/edit a player's ender chest, online or offline

Both are recursive (you can open shulker boxes and bundles found inside), work even when the target is offline (edits are written straight to their .dat file, with an automatic .bak backup taken first, and the player is prevented from logging in while being edited), and are Geyser/Floodgate-aware so Bedrock accounts resolve too.

Four commands, all opened in a chest-style paginated GUI (45 results per page with in-GUI "previous/next page" buttons plus a natural-generation filter toggle) for any player who runs them, sorted closest-first. Every scan covers the entire server, not just what's currently loaded:

  • /chestfind [item_id] [x] [y] [z] - lists every storage container on the entire server (chests, trapped chests, barrels, shulker boxes, hoppers, dispensers, droppers, furnaces, brewing stands, ...), closest to a point of your choosing first. Double chests are listed once, with their combined contents. Usable from console (as a plain-text summary, printed once the full scan finishes, since console has no inventory to open a GUI in). Left-click a result to teleport to it; right-click to open the container itself right there in the GUI (recursively - shulker boxes/bundles inside it open too, same as /invsee) without teleporting at all.
  • /chestfindnearby [item_id] [radius_x] [radius_y] [radius_z] - the same, but always centred on your current position, and can be narrowed to a box around you instead of the whole server. Aliases: /chests, /cfn. Players only (it needs your location).
  • /blockfind <block_id> [x] [y] [z] - like /chestfind, but finds every occurrence of a specific block instead of containers (the block id is mandatory). Alias: /bf.
  • /blockfindnearby <block_id> [radius_x] [radius_y] [radius_z] - a mix of /blockfind and /chestfindnearby: mandatory block, centred on you, optional radius. Alias: /bfn.

The item/block id argument is optional wherever shown in brackets - e.g. /chestfindnearby 50 50 50 goes straight to a 50-block radius search with no item filter, exactly like /chestfindnearby diamond 50 50 50 does with one. A single radius number (/chestfindnearby 50) applies uniformly to all three axes; give three numbers for separate X/Y/Z radii.

Each result's tooltip shows its world, exact coordinates, distance from you, and (for containers) how many slots are filled and how many of the searched-for item it holds.

Hiding naturally-generated results

Between the previous/next page buttons sits a toggle button (a grey/lime dye) that hides or shows results sitting inside a naturally-generated structure - villages, dungeons, mineshafts, shipwrecks, temples, ruins, fortresses, bastions, end cities, ancient cities, trial chambers, and so on. It's off (hidden) by default, so a fresh search starts by showing only things that plausibly look player-placed, not vanilla loot generation. This needs no item tracing and works immediately even on worlds that were already fully generated before this plugin was ever installed - it reads three kinds of data Minecraft already permanently records, none of it placement-tracking:

  1. Structure membership - most loot-bearing structures (villages, mineshafts, temples, fortresses, end cities, ...) are recorded per-chunk by the game itself (Chunk#getStructures()), so "is this inside a generated structure's bounding box" is a direct, reliable check. This also keeps working with most custom/plugin world generators, since it's the same data vanilla's own structure-locate/map APIs rely on.
  2. Unresolved loot table - a container nobody has opened yet still carries the loot table it generated with (Lootable#getLootTable()), which Bukkit exposes directly. This catches natural loot generically, including things structure membership alone can't (see next point) - but only until the container is opened for the first time, since opening it resolves and clears the tag.
  3. Spawner proximity - plain "dungeons" (the mossy-cobblestone monster-spawner room) are a genuine gap in signal 1: they're a world-gen feature, not a jigsaw structure - not disabled by the "Generate Structures" option and not findable with /locate either, confirmed against Minecraft's own documentation. There is no structure entry to find for one. A dungeon chest within a few blocks of its spawner is therefore treated as natural too, as a last-resort signal for exactly the case where signal 2 no longer applies (an already-opened dungeon chest).

Toggling the filter is purely a display switch - every result is always found and collected regardless of it, so switching it never triggers extra scanning.

This only reliably works for containers. For blocks, only ones inside a structure's bounding box are ever marked natural - there's no equivalent for ordinary terrain generation like ore veins, since vanilla doesn't record "this block came from worldgen" anywhere accessible without regenerating the chunk against the world's seed. Blocks outside a structure are always treated as "not natural" (shown), which is the safe default - it never hides something that might be worth a closer look.

Minimum-item filter

Also between the previous/next buttons (containers only - /chestfind and /chestfindnearby) is a second toggle that opens a double-chest-sized editor: put items in it - say, 5 netherite blocks - and close it, and the results list narrows to only containers holding at least that many of every item you put in (aggregated across every slot in the container, ignoring things like enchantments/durability/names - only material and amount matter). Put in nothing and close it to clear the filter again. Every item placed in the editor is always handed straight back to you the moment you close it - it's a scratch space for specifying criteria, not a real container; nothing is ever kept or consumed, and if your own inventory happens to be full when it hands things back, the leftover is dropped at your feet rather than lost. Like the natural-generation filter, this needs no extra scanning: every container's full item breakdown was already recorded when it was found, so the filter is purely a display-time narrowing.

How the whole-server scan works

For a player, a scan happens in two phases, both feeding the same already-open GUI:

  1. Instant: everything already loaded right now is scanned immediately, and the GUI opens with those results as soon as that finishes - typically well under a second.
  2. Background, on demand: every other chunk that was ever generated and saved to disk for the world(s) being searched is found by reading region-file headers (cheap - no NBT parsing), then loaded, scanned, and unloaded again in small throttled batches - but only as many as are needed to stay one page ahead of whatever you're currently looking at. Once enough results exist to fill the current page plus a lookahead page, the background scan pauses itself rather than continuing to grind through the rest of the server for no one; turning to the next page wakes it back up. The info item in the GUI reflects this ("Scanning...", "Paused - more results available", or "Whole server scanned" once every chunk really has been covered). Closing the GUI stops the background scan for good. Console has no paging to drive this, so a console-run command always scans the entire server to completion before printing its one summary. Chunks this scan itself loads are unloaded again afterwards; chunks that were already loaded for some other reason are left alone, and a chunk is held with a temporary loading ticket for the whole time it's being scanned so it can't be unloaded out from under the scan by the server itself.

Two tunables in config.yml control how fast/aggressive the background phase is (there is no cap on how much of the server gets covered - only on how quickly):

find:
  max-results: 500              # how many results (closest first) are ever shown/paginated at once
  chunk-batch-size: 50          # chunks loaded+scanned together per batch
  chunk-batch-delay-ticks: 2    # delay between batches (20 ticks = 1s); raise to go easier on the server

A hardcoded (non-configurable) safety ceiling of 50,000 total matches also exists purely to avoid running out of memory on something like a wide-open /blockfind stone - if you ever hit it, the GUI will say so and the fix is narrowing the search (radius/coords, or a rarer block), not raising a number.

Everything under stolenitemadmin.find gates these four commands; stolenitemadmin.invsee / stolenitemadmin.endersee gate the two viewer commands; stolenitemadmin.* grants all of it. All default to op.

Compatibility / Folia notes

This plugin is written entirely against the standard Paper/Folia scheduler API (RegionScheduler, GlobalRegionScheduler, Entity#getScheduler()), not internal Folia classes, so the same jar runs correctly on both plain Paper and Folia:

  • Container scans read live block state on each chunk's owning region thread (via Bukkit.getRegionScheduler()), so contents are always current and thread-safe on Folia.
  • Block-type scans only need block types, not live state, so the owning thread is used just to take a cheap chunk snapshot; the actual (potentially large) block-by-block comparison then runs off that thread entirely, so a big scan never blocks a region tick.
  • The background phase loads not-yet-loaded chunks with the async chunk API (World#getChunkAtAsync), never the blocking synchronous loader, and always unloads them again through the owning region thread afterward.
  • Region-file header reads (used to discover every chunk ever generated) are plain java.io disk access on a small dedicated thread pool - not Bukkit/Paper API at all - so they never need to run on any particular thread and never block a game/region thread.
  • Teleporting to a result always uses Player#teleportAsync, which Folia requires for cross-region/cross-world teleports and which behaves identically on plain Paper.
  • GUI opening/closing/refreshing is always dispatched through the relevant player's own entity scheduler.

Information

Category
Admin Tools
Published
August 15, 2026
License
1Downloads
0Stars
Supports Folia

Pinned Versions

Members

1