A Vintage Story mod that opens every storage container around you in a single dialog, so you can search, sort and shift-click across all of them at once instead of one chest at a time.
Press R (rebindable under Controls → "[PackRat] Open All Containers") and every container you can reach opens in one Storage Browser window. Press it again to close.
What counts as "reachable" depends on where you are standing:
- Inside an enclosed room, every container in the room opens, even if a pillar or a wall corner is between you and it.
- Out in the open, containers within about 5 blocks open, as long as you can actually see them.
In either case, containers sealed inside a room you are not standing in are left alone, even if you can see and reach them through a doorway. A room's storage belongs to whoever is in the room.
Containers locked against you by block reinforcement are skipped.
The browser has a search box and a sort dropdown (alphabetical, by category, by material), and shows which slots belong to which container.
Shift-clicking an item into the browser sends it to whichever container suits it best. By default that means:
- A container that already holds that item wins - your stack gets topped up rather than scattered into a new container.
- Failing that, a container that already has free space wins.
- A wholly-empty crate is the last resort, because putting anything into an empty crate locks that whole crate to a single item type.
- Perishable food overrides all of the above and goes to whichever container preserves it best - a cellar, a storage vessel, an ice box. This accounts both for how cool the spot is and for containers that slow spoilage on their own, so a storage vessel beats a plain chest standing right next to it.
If you want a different order between container types, set one:
.packrat priority set trunk,chest,crate
Highest priority first. Types you do not list rank below every type you do list.
Priority only decides between equally good targets. It never overrides rule 1 or rule 4 above: if a crate already holds your nails, the nails go in that crate even when trunks are top of your list, and cheese still goes in the cellar. Priority decides where things land when nothing already holds them - which container gets claimed for a new item type, and which empty container gets used first.
The names are block codes, and they are not always what you would guess - a Basket is
stationarybasket, and a Trunk is trunk rather than a kind of chest. To see the
correct token for everything around you:
.packrat priority types
Container types in reach:
chest - "Chest" x4
crate - "Crate" x7 [priority 3]
stationarybasket - "Basket" x1
trunk - "Trunk" x2 [priority 1]
Use the token on the left with '.packrat priority set'.
| Command | What it does |
|---|---|
.packrat priority |
Show the current priority list |
.packrat priority set <types> |
Set the order, highest first, comma or space separated |
.packrat priority reset |
Clear the list and go back to defaults |
.packrat priority types |
List the container types in reach and their tokens |
.packratdebug |
Toggle debug logging |
Your priority list is per-player and saved in ModConfig/packrat-client.json in your
Vintage Story data folder.
Packrat must be installed on the server as well as the client. This is not optional for the priority feature: when you shift-click, the game does not tell the server where the items went - it tells the server only which slot you clicked, and the server works out the destination itself. Your priority list is sent to the server so that both sides reach the same answer.
Packrat picks up containers from Sortable Storage, Containers Bundle, Better Crates, QP's Storage Controller (including its linked containers) and Primitive Survival's tree hollows, alongside all the vanilla ones.
Empty crates are no longer the preferred shift-click target. Previously an empty crate outranked any container that merely had free space, and ranked level with a container that already held the item - so shift-clicking could claim a fresh crate for a single stack while a trunk sat there with room, and which one you got could depend on the order the containers happened to open in. Now an empty crate ranks last, and items go to containers with existing stacks or existing space first.
If you preferred the old behavior - crates first, always - you can ask for it explicitly:
.packrat priority set crate
Shift-clicking also now prefers open containers over your own backpack more consistently than before, where the two used to score equally and the winner depended on the order things were opened in.
./build.shThe built mod lands in the Releases directory.
tests/ holds an in-game suite that runs against a real, running Vintage Story
world via vstestkit:
cd ../vstestkit
# the seams, headless
bash scripts/run.sh ../Packrat/tests --mod ../Packrat/Packrat
# plus the storage mods Packrat claims to support, and both game sides
cairn-cli sync packratcompat
bash scripts/run.sh ../Packrat/tests --mod ../Packrat/Packrat \
--mods ~/.cairn/packs/packratcompat/Mods --clientWhy bother, when the mod compiles. Almost none of Packrat's risk is in its own logic — it is in the seams where it reaches into the game and into other mods by name, every one of which compiles perfectly once the target has moved:
- Four Harmony patches on an overload of
InventoryBase.GetBestSuitedSlotselected by argument types. Add a parameter upstream and the attribute matches nothing; shift-click routing silently reverts to vanilla. OnReceivedServerPacketintercepted by name and(int, byte[])signature, andOnPlayerRightClickinvoked reflectively.- Six mod container types resolved from string type names. These are designed to fail quietly — Packrat must not hard-depend on six optional mods — so a renamed type does not break anything, it just stops being supported, and the only sign is a missing line in a log nobody reads.
| suite | asserts |
|---|---|
PackratIntegration |
the patch targets still exist, all four patches attach, each exactly once, and chests and crates are still the block entity types the scan registry keys on |
PackratModCompat |
each supported mod's container type resolves and lands in Packrat's scan registry, checked against the real mods rather than a string comparison |
PackratInsertPriority |
where a shift-click lands — existing stack beats empty, an empty crate ranks last, a mismatched crate is refused outright, block containers outrank a positionless inventory, and no two containers ever score exactly equal |
PackratRooms |
a sealed shell registers as a room with no exits, a container in one preserves food better, and the perish bonus matches the normalised curve exactly |
PackratDiscovery |
which containers the hotkey opens — in-room, range-limited outdoors, non-storage block entities excluded, and a container sealed in a closet left to that closet. Needs --client |
The priority and room suites assert on the weight GetBestSuitedSlot returns
rather than on where an item visibly ends up, because that is where the rules
actually live — the README states them in words, the mod expresses them as
arithmetic on that weight. It is also exact, and does not depend on the order
containers happened to open in.
Two of those are worth knowing about if you change the weighting:
- No two containers may ever tie. A shift-click's destination is never transmitted — the packet carries only the source slot — so the server re-derives it by running the same weighting. A tie resolves to whichever inventory was opened last, an order the two sides do not reliably share, so a tie lets client and server pick different containers and the items snap back on the next sync.
- The perish bonus is pinned to its formula, not just its direction. It used
to be
Math.Max(0f, (1f - rate) * 10f), which hands every rate at or above 1.0 the same zero — so in a warm spot the preference silently did nothing. A test that only checks "the cellar wins" cannot tell the two formulas apart; one that reproduces the curve can.
The compat suite needs the packratcompat pack (Primitive Survival and QP's
Storage Controller). Without it each test logs "this test proved nothing" and
passes, so read the log line rather than the green tick.
PatchesAreRegisteredExactlyOnce is [RequiresClient] on purpose. Start()
runs once per side and in singleplayer both sides resolve the same assembly, so
a bare PatchAll() there registers everything twice — headless has one side and
cannot tell the difference. Packrat guards this with Harmony.HasAnyPatches;
the test pins that guard.
Copy the .zip from Releases into your Vintage Story Mods folder.