Skip to content

Soulbinding

Belongs to: Server Owner Guide · Related: Item Definition · Configuration File

Soulbinding binds an item to a player so that only the owner can use it, and it cannot be dropped / traded / dropped on death. Even soulbound items placed inside a shulker box are protected.


1. How Binding Is Triggered

MethodDescription
Auto-bind on pickupWhen an item has options.bind_on_acquire: true and soulbound.auto-bind-on-pickup: true, an unowned item is automatically bound when picked up by a player
Command/qi soulbind [level] binds the main-hand item to yourself; /qi unsoulbind unbinds
APIQinhItemsAPI.bindSoulbound(item, player, level)

The binding state is stored in the item's instance data: soulbound_owner (UUID string), soulbound_level (integer level).

yaml
my_legendary:
  options:
    bind_on_acquire: true      # Bind on pickup

2. Protection Rules

SoulboundProtectionListener intercepts the following actions (for non-owners):

ActionEventIntercept Condition
Drop bound itemPlayerDropItemEventOwner mismatch → reject
Drop container holding a bound itemPlayerDropItemEventContainer holds a bound item → reject
Pick up another player's bound itemEntityPickupItemEventOwner mismatch → reject
Move bound item in inventoryInventoryClickEventOwner mismatch → reject and refresh inventory
Drag bound itemInventoryDragEventSame as above
Break a container block holding a bound itemBlockBreakEventContainer holds a bound item → reject
Death dropPlayerDeathEventThe owner's bound items are removed from drops and stored for pending return
Return on respawnPlayerRespawnEventBound items are returned to the inventory; overflow drops naturally

Bypass permission: Players holding qinhitems.bypass.soulbound (default value see config) skip all restrictions.


3. Container Deep Scan

SoulboundContainerScanner recursively scans nested containers (shulker boxes), up to 8 levels deep, to prevent bypassing protection by "stuffing a bound item into a shulker box and then dropping / breaking it". On death it extracts the owner's bound items, and on respawn it returns them.

Controlled by soulbound.scan-containers: true (enabled by default).


4. Configuration Options

The soulbound section of config.yml:

yaml
soulbound:
  enabled: true                          # Master switch
  lore-format: "&7{owner}'s item"        # Bound Lore template, placeholders {owner} {level}
  auto-bind-on-pickup: true              # Auto-bind on pickup (requires item bind_on_acquire)
  scan-containers: true                  # Deep-scan containers
  bypass-permission: "qinhitems.bypass.soulbound"
  message-cooldown-ticks: 20             # Reject-message cooldown (ticks), 20×50=1000ms
  messages:
    drop: "&cThis item is soulbound and cannot be dropped."
    pickup: "&cThis item belongs to {owner}; you cannot pick it up."
    move: "&cThis item belongs to {owner}; you cannot move it."
    container: "&cThis container holds a bound item and cannot be broken."
    container-drop: "&c\"{container}\" holds a bound item and cannot be dropped."
KeyDefaultMeaning
enabledtrueMaster switch
lore-format&7{owner}'s itemBound Lore line ({owner} player name, {level} level)
auto-bind-on-pickuptrueAuto-bind on pickup
scan-containerstrueDeep-scan nested containers
bypass-permissionqinhitems.bypass.soulboundBypass permission
message-cooldown-ticks20Interval between repeated reject messages
messages.*see aboveReject messages for each scenario

5. Lore Display

Once bound, a line is appended to the item's Lore (per lore-format):

§7Steve's item

It is rendered by the "Soulbinding" segment of the structured Lore, reading the variables soulbound.owner / soulbound.level.

🖼️ [Image placeholder] Bound item hover, with "xxx's item" shown at the bottom · suggested assets/soulbound-lore.png


6. Developer API

kotlin
QinhItemsAPI.isSoulbound(item): Boolean
QinhItemsAPI.getSoulboundOwner(item): UUID?
QinhItemsAPI.bindSoulbound(item, player, level = 0)
QinhItemsAPI.clearSoulbound(item)

💡 For market / mail plugin authors: Check isSoulbound before listing / mailing; bound items should not be traded. See API Reference.


Next Steps