Inventory Rollback
The invrollback module gives staff the one thing a survival server almost always ends
up wanting a separate plugin for: a way to hand a player back an inventory they lost —
to a lag death, a bad restart, a bug — without a manual /give guessing game. It
snapshots a player's inventory at the moments things go wrong (a death, a logout),
keeps a bounded history of those snapshots in the database, and lets staff browse and
restore them from a GUI with /invrestore <player>. It's the AxInventoryRestore
feature set, built into the same module system as everything else and configured from
one HOCON file, modules/invrollback/config.conf.
Ships enabled and working
Unlike the villagers and security modules, invrollback is on and doing something
out of the box: a fresh install captures a snapshot on every death and every logout
straight away, so a snapshot already exists the first time you need one. Turn the
whole module off with enabled = false, or narrow when it captures with the
capture block below.
Every snapshot is DB-backed and never stored in PDC — the same hard invariant the
economy and vaults ledgers hold. The authority is the stored row, not the live
inventory, so a snapshot survives a world rollback: roll the world back to last
night and the snapshot you took this morning is still there to restore from. The
snapshots live in the inv_snapshots table, which ships as part of the persistence
baseline, so the module runs no migrations of its own.
What a snapshot captures¶
A snapshot freezes the player's full inventory at the moment it's taken:
- the main inventory — hotbar and storage,
- the armour they're wearing,
- the offhand slot,
- and, when
include-enderchestis on, the ender chest alongside it, - plus where the player was standing, so staff can go back to the scene.
Each item is serialised whole — components, enchantments, custom names and all — so a restored item is byte-for-byte the one that was lost, not a re-rolled stand-in. Empty slots store nothing, so a near-empty inventory stays compact in the table.
Every capture is tagged with a cause — DEATH, LOGOUT, or RESTORE — which the
restore GUI shows so staff can tell a death snapshot apart from a routine logout one.
When a snapshot is taken¶
Two triggers are under your control, and a third happens automatically as a safety net.
On death¶
With it on, the player's inventory is read the instant they die, before the items
drop, and frozen into a DEATH snapshot. The capture is a pure observer — it runs at
the lowest priority and never alters the death, the drops, or the death message; it only
reads. This is the trigger you'll restore from most often.
On logout¶
With it on, the player's inventory is frozen into a LOGOUT snapshot when they leave
the server — a cheap "last known good" state that covers a crash, a kick, or a dupe that
only shows up after they've logged off.
The pre-restore safety snapshot¶
There's a third cause you never configure: RESTORE. Immediately before a restore
overwrites a player's live inventory, the module snapshots their current inventory
first, tagged RESTORE. So a restore is itself undoable — pick the wrong snapshot and
you can restore the RESTORE copy to put things back exactly as they were. This safety
copy is only taken when the chosen snapshot actually resolves; a dead click on an
already-pruned snapshot writes nothing.
Captures never block the server
The live inventory is read on the region tick thread (where reading it is legal), serialised there, and the database write is then handed to a background thread. A capture never blocks a region thread and is Folia-safe from line one.
Restoring an inventory — /invrestore <player>¶
Staff holding uxmessentials.invrollback.restore run /invrestore <player> to
open the restore GUI for a target. The whole flow is three steps:
- The snapshot list. A paginated window lists every snapshot the target holds, newest first, one icon each. The icon's material tracks the cause — a skeleton skull for a death, an ender pearl for a logout, a clock for a pre-restore safety copy — and its label names the cause and the capture time, with a click-to-preview hint. If the target has no snapshots, you get a "no snapshots" line instead of an empty window.
- The preview. Click a snapshot to open a read-only preview: a chest window whose top five rows mirror the snapshot's inventory exactly as it was, with a lime restore button in the bottom row. Every other click is cancelled, so you can look without touching — nothing you do in the preview changes anything until you press the button.
- The restore. Press the button and the target's live inventory (main, armour, offhand, and the ender chest when the snapshot carried one) is overwritten with the snapshot's contents — after the pre-restore safety snapshot is taken first. You get a confirmation line naming the player and the cause you restored.
The target must be online
A restore is applied to a live inventory — it's never written to the player's
saved file on disk. So the target has to be online for the restore to land; aim
/invrestore at an offline or unknown name and you get a "not online" line and
nothing changes. Their snapshots persist regardless, so the restore succeeds the
moment they rejoin — open the GUI again once they're back.
The target lookup, the snapshot listing, and both database touches during a restore all
run off the tick thread, so /invrestore never blocks the server.
Two more things you can do with a snapshot¶
Both act on one snapshot chosen by its list index (1 is the newest), and both work
on an offline target, because they read stored data and act on you, not on the
target's live session.
Export to shulker boxes¶
Packs the snapshot's items into named shulker boxes and hands them to you, so a lost inventory can be inspected or given back by hand without touching the target's live inventory. Every store is exported (hotbar, storage, armour, offhand and the ender chest), packed 27 stacks to a box, so a full inventory produces as many boxes as it needs. An item that is itself a shulker box is handed over as-is rather than nested, because vanilla forbids a box inside a box. Boxes that will not fit are dropped at your feet.
Gated on uxmessentials.invrollback.export.
Teleport to the scene¶
Teleports you to where that snapshot was captured, which is the fast way to find a death
site or check what a logout looked like. Gated on
uxmessentials.invrollback.teleport.
Older snapshots have no location
Location capture arrived after the module shipped, so a snapshot taken before it, or one whose world is no longer loaded, answers with a "no location" line instead of teleporting.
Retention¶
Left alone, a snapshot-per-death-and-logout would grow the table without bound, so retention keeps it in check two ways, and either can be switched off independently.
| Key | Default | What it does |
|---|---|---|
max-per-player |
10 |
The most snapshots kept per player. After every capture the player's snapshots past this count are pruned oldest-first, so a player's history is trimmed the moment it overflows — you always keep their most recent 10. 0 disables the count cap. |
max-age-days |
30 |
The oldest a snapshot may get. A scheduled sweep removes any snapshot older than this. 0 disables the age cap. |
The count cap is enforced immediately on every capture, so between sweeps only the
age half can accrue. On top of that, an hourly sweep runs the retention policy in the
background: it re-applies the count cap across every player (catching anyone left over
the cap after you lower max-per-player and reload) and removes anything past
max-age-days. The first sweep runs a few minutes after the server starts so it never
collides with startup, then every hour after. Set both limits to 0 and the sweep
never schedules at all.
The config.conf¶
The complete modules/invrollback/config.conf, with the shipped defaults:
enabled = true
# When to take a snapshot.
capture {
on-death = true # snapshot the inventory when a player dies (read before the items drop)
on-logout = true # snapshot the inventory when a player leaves the server
}
# Whether a snapshot also stores the player's ender chest alongside the main inventory, armor and offhand.
include-enderchest = true
# How long snapshots are kept, so the table stays bounded. After each capture the player's snapshots past
# max-per-player are pruned oldest-first; a scheduled hourly sweep removes any snapshot older than max-age-days
# (and trims any player left over the count cap after a reload). Set either limit to 0 to disable it.
retention {
max-per-player = 10
max-age-days = 30
}
Config is loaded once on enable and swapped atomically on /uxmess reload invrollback,
so a capture handled mid-reload always sees one coherent snapshot. Delete a line and it
falls back to the shipped default above.
Command¶
| Command | What it does | Permission |
|---|---|---|
/invrestore <player> |
Open the inventory-snapshot restore GUI for a player | uxmessentials.invrollback.restore |
/invrestore export <player> <index> |
Pack that snapshot into shulker boxes and give them to you | uxmessentials.invrollback.export |
/invrestore tp <player> <index> |
Teleport to where that snapshot was captured | uxmessentials.invrollback.teleport |
The <player> argument suggests online players, but accepts any name the server has seen
before. /invrestore itself is visible to anyone holding any of the three nodes, and
each verb then gates on its own.
Only the restore needs the target online, since it writes their live inventory. Listing, exporting and teleporting all work while they are offline.
Permissions¶
Three action nodes, plus the per-module reload gate. All default to op, so a fresh
install hands the restore tools to operators and nobody else — grant the specific node to
the staff rank that should have it. Splitting them lets you hand out the read-only verbs
(export, teleport) without the one that overwrites a live inventory.
| Node | Default | What it grants |
|---|---|---|
uxmessentials.invrollback.restore |
op |
/invrestore <player> — open the snapshot GUI and restore from it |
uxmessentials.invrollback.export |
op |
/invrestore export <player> <index> — pack a snapshot into shulker boxes |
uxmessentials.invrollback.teleport |
op |
/invrestore tp <player> <index> — teleport to the capture location |
uxmessentials.module.invrollback |
op |
Reload / inspect the module (/uxmess reload invrollback) |
Next Steps¶
- 🔑 Permission Reference — the full node list
- 🧩 Per-Module Config — where
modules/invrollback/config.conflives and how it's loaded - 🗄️ Vaults — DB-backed player-owned item storage, the same "survives a rollback" guarantee
- 🛡️ Moderation — bans, mutes and warnings for the staff who hold the restore node