Skip to main content
The ItemListener intercepts packets that update individual item slots, allowing real-time formatting of item display names and lore when items are moved, picked up, or modified in the player’s inventory or cursor.

Packet Type

Intercepts: PacketType.Play.Server.SET_SLOT This packet is sent when:
  • A single item slot is updated
  • The player picks up an item
  • An item is placed on the cursor
  • A specific inventory slot changes

Configuration

listeners:
  items: true
When disabled, individual item updates are sent unmodified to the client.

Class Structure

public class ItemListener extends AbstractListener {

    public ItemListener(ProcessHandler processHandler, ConfigManager configManager) {
        super(processHandler, configManager);
    }

    @Override
    public void onPacketPlaySend(PacketPlaySendEvent e) {
        // Implementation
    }
}
Source: ItemListener.java:10-26

Implementation Details

1. Packet Type Validation

if (!config.getOrDefault("listeners.items", true) ||
    e.getPacketType() != PacketType.Play.Server.SET_SLOT) return;
Source: ItemListener.java:18-19 The listener exits early if:
  • The listener is disabled in the configuration
  • The packet type doesn’t match SET_SLOT

2. Item Processing

Player player = e.getPlayer();
WrapperPlayServerSetSlot packet = new WrapperPlayServerSetSlot(e);
packet.setItem(handler.processItem(packet.getItem(), player));
Source: ItemListener.java:21-23 For each item slot update:
  1. Wraps the packet in WrapperPlayServerSetSlot
  2. Extracts the item stack from the packet
  3. Processes it through ProcessHandler.processItem()
  4. Sets the formatted item back to the packet

Difference from InventoryListener

ItemListener

  • Handles single item updates
  • Uses SET_SLOT packet
  • Processes one item at a time
  • Triggered by individual slot changes
  • Uses processItem() method

InventoryListener

  • Handles bulk inventory updates
  • Uses WINDOW_ITEMS packet
  • Processes multiple items at once
  • Triggered when inventory is opened/refreshed
  • Uses processItems() method
Both listeners work together to ensure comprehensive item formatting coverage.

Usage Examples

Item Pickup

When a player picks up an item:
ItemStack item = new ItemStack(Material.DIAMOND);
ItemMeta meta = item.getItemMeta();
meta.displayName(Component.text("<rainbow>Rare Diamond</rainbow>"));
meta.lore(List.of(Component.text("Value: $%item_value%")));
item.setItemMeta(meta);

player.getWorld().dropItem(player.getLocation(), item);
When the player picks it up:
  1. Server sends a SET_SLOT packet
  2. ItemListener intercepts it
  3. Processes the rainbow formatting
  4. Resolves the %item_value% placeholder
  5. Player sees the formatted item in their inventory

Cursor Item

When a player clicks an item in a GUI:
inventory.setItem(0, item);
// Player clicks the item
// SET_SLOT packet sent for cursor
The listener ensures the item on the player’s cursor is properly formatted.

Slot Updates

When programmatically updating a player’s inventory:
ItemStack sword = new ItemStack(Material.DIAMOND_SWORD);
ItemMeta meta = sword.getItemMeta();
meta.displayName(Component.text("<gradient:red:gold>Legendary Blade</gradient>"));
meta.lore(List.of(
    Component.text("<gray>Damage: <red>%weapon_damage%"),
    Component.text("<gray>Durability: <green>%weapon_durability%")
));
sword.setItemMeta(meta);

player.getInventory().setItem(0, sword);
The listener will:
  1. Intercept the slot update
  2. Process the gradient formatting in the name
  3. Resolve %weapon_damage% and %weapon_durability% placeholders
  4. Apply the color codes in the lore

Processing Flow

1

Slot Changed

Server sends a SET_SLOT packet for an item update
2

Interception

ItemListener intercepts the packet
3

Item Extraction

Extracts the ItemStack from the packet
4

Component Processing

Processes item display name and lore:
  • Resolves placeholders
  • Parses MiniMessage formatting
5

Packet Update

Sets the formatted item back to the packet
6

Client Update

Modified packet sent to client, item appears formatted

Per-Player Item Data

Since processing includes the player parameter, items can display personalized data:
meta.lore(List.of(
    Component.text("Owner: %player_name%"),
    Component.text("Power Level: %player_level%"),
    Component.text("Bound to: %player_uuid%")
));
Each player who views or receives the item sees contextual information based on their own data.

Performance Considerations

The ItemListener processes individual slot updates, which can be frequent. The listener includes early returns to minimize performance impact:
  • Config check happens before packet deserialization
  • Only SET_SLOT packets are processed
  • Processing is skipped entirely when disabled

When is SET_SLOT Used?

The SET_SLOT packet is sent in many scenarios:
  1. Inventory Changes
    • Player picks up an item
    • Item is added to inventory
    • Item is removed from inventory
  2. Cursor Updates
    • Player clicks an item in a GUI
    • Item is placed on the cursor
    • Item is taken from the cursor
  3. Specific Slot Updates
    • Plugin updates a single inventory slot
    • Equipment slot changes
    • Offhand item updates
  4. Item Modifications
    • Item durability changes
    • Item enchantments updated
    • Item metadata modified

Dependencies

  • PacketEvents: For packet interception
  • WrapperPlayServerSetSlot: Packet wrapper for slot update packets
  • ProcessHandler: For placeholder resolution and formatting
    • processItem() - For single item processing

Common Use Cases

  1. RPG Items
    • Dynamic weapon stats based on player level
    • Personalized equipment descriptions
    • Real-time durability indicators
  2. Custom Items
    • Formatted item names with gradients
    • Lore with placeholder data
    • Item value displays
  3. Economy Items
    • Price tags with current economy data
    • Ownership information
    • Trade status indicators
  4. Quest Items
    • Progress indicators in lore
    • Quest stage information
    • Objective descriptions

Build docs developers (and LLMs) love