Live
Item integrity and exploit hardening
Keeping player items safe on a live server: a zero-loss migration of every vehicle trunk, an equipment system that recovers stuck items, and fixes for the ways players were duplicating or teleporting items.
- Role
- Design and implementation, working solo
- Stack
- Lua (GLua), NutScript, MySQL
- Context
- Diverge Networks, a live roleplay server with a player-driven economy
- Status
- Live
The problem
On a roleplay server, items are the economy. A bug that loses an item costs a player hours of play, and a bug that duplicates one devalues everyone else’s. I owned the item layer end to end: moving player storage to a new model without losing anything, and closing the gaps players had found.
Migrating every trunk without losing an item
Each vehicle used to have its own trunk. They were replaced by one shared trunk per character, migrated automatically the first time each character loads after the update.
- Items move one at a time, in sequence, so no two transfers ever race each other.
- If the shared trunk is full, the item goes to a lost-and-found inventory at the car dealer instead of failing.
- If a move fails, the item goes back to its original slot. If even that fails, the server raises an error naming the item.
- The vehicle’s database row only switches to the shared trunk after every item has moved, so a failure halfway through leaves the old trunk as the source of truth.
- Every step is written to a dated migration log.
Video coming soon
A test character with items spread across several vehicle trunks loads in. The shared trunk opens with everything consolidated, then the lost-and-found at the car dealer shows the overflow.
assets/projects/item-integrity/trunk-migration.mp4 (20 to 30 seconds)
Screenshot coming soon
The shared 10 by 10 trunk inventory, partly filled.
assets/projects/item-integrity/shared-trunk.png (tight crop to the panel)
Code excerpt coming soon
The item transfer with fallback and revert, and the migration that only updates the database once every item has moved.
assets/excerpts/trunk-migration.lua
The trade-off
Removing an item and adding it elsewhere isn’t one database transaction, so a server crash between the two steps could still lose that one item. The revert path and the migration log keep that window small and visible, and it was an acceptable risk for a one-time migration.
Equipment that can’t get lost
Equipping clothing moves an item out of the inventory grid into a hidden state. When something went wrong mid-way, items vanished. I rebuilt equipping and unequipping as one promise-based API used by every item type, and added a check on character load that finds items stuck in that hidden state, puts them back, and tells the player what happened.
Closing duplication and teleport exploits
- Shipment duplication. Taking an item from a crate started an asynchronous add without reserving it first, so fast repeated requests could duplicate items. The count is now reserved and the crate locked before the async work, and refunded if the item doesn’t fit.
- Trunk teleporting. Having two vehicles’ trunks open at once let players move items between them from anywhere. Only one trunk can be open at a time now.
- Ghost items. An admin inventory reset could leave slots marked as occupied by items that no longer existed. The grid is now cleared once every removal has finished.
Inventories that fit on the wire
Large inventories were overflowing network buffers when synced to players. I moved inventory and item sync to encoded, compressed payloads with an explicit length prefix.
Screenshot (optional) coming soon
A few lines from the migration log, with player names and IDs removed.
assets/projects/item-integrity/migration-log.png (crop to the relevant lines)