> For the complete documentation index, see [llms.txt](https://vulkan-technologies.gitbook.io/documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://vulkan-technologies.gitbook.io/documentation/vulkan-menu/api/itemstack-provider.md).

# ItemStack Provider

ItemStack Providers allow you to create custom item sources for VulkanMenu. This enables integration with custom item systems, databases, or any other source of items beyond the standard Minecraft materials.

## Understanding ItemStack Providers

An ItemStack Provider is responsible for converting a string identifier into a Bukkit ItemStack. VulkanMenu uses these providers to support various item sources like ItemsAdder, Oraxen, Nexo, and HeadDatabase.

## The ItemStackProvider Interface

```java
package com.vulkantechnologies.menu.model.provider;

import org.bukkit.inventory.ItemStack;
import org.spongepowered.configurate.serialize.SerializationException;

public interface ItemStackProvider {
    
    /**
     * Provides an ItemStack based on the given value string
     * @param value The identifier string (without prefix)
     * @return The created ItemStack
     * @throws SerializationException if the item cannot be created
     */
    ItemStack provide(String value) throws SerializationException;
    
    /**
     * Returns the prefix used to identify this provider
     * @return The provider prefix (e.g., "custom-")
     */
    String prefix();
}
```

## Creating a Custom Provider

### Basic Implementation

Here's a simple custom item provider:

```java
import com.vulkantechnologies.menu.model.provider.ItemStackProvider;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.spongepowered.configurate.serialize.SerializationException;

public class CustomItemProvider implements ItemStackProvider {
    
    @Override
    public ItemStack provide(String value) throws SerializationException {
        // Parse the custom item identifier
        if (value.equals("special_sword")) {
            ItemStack item = new ItemStack(Material.DIAMOND_SWORD);
            ItemMeta meta = item.getItemMeta();
            meta.setDisplayName("§6Special Sword");
            meta.setLore(Arrays.asList("§7A powerful weapon", "§7forged by masters"));
            item.setItemMeta(meta);
            return item;
        }
        
        throw new SerializationException("Unknown custom item: " + value);
    }
    
    @Override
    public String prefix() {
        return "custom-";
    }
}
```

Usage in configuration:

```yaml
items:
  special:
    slot: 10
    material: "custom-special_sword"
```

### Database-Based Provider

Create a provider that loads items from a database:

```java
public class DatabaseItemProvider implements ItemStackProvider {
    
    private final Map<String, ItemStack> cache = new HashMap<>();
    private final Connection database;
    
    public DatabaseItemProvider(Connection database) {
        this.database = database;
    }
    
    @Override
    public ItemStack provide(String value) throws SerializationException {
        // Check cache first
        if (cache.containsKey(value)) {
            return cache.get(value).clone();
        }
        
        try {
            // Query database for item data
            PreparedStatement stmt = database.prepareStatement(
                "SELECT material, name, lore, enchantments FROM items WHERE id = ?"
            );
            stmt.setString(1, value);
            ResultSet rs = stmt.executeQuery();
            
            if (rs.next()) {
                ItemStack item = createItemFromResultSet(rs);
                cache.put(value, item);
                return item.clone();
            }
            
            throw new SerializationException("Item not found in database: " + value);
            
        } catch (SQLException e) {
            throw new SerializationException("Database error: " + e.getMessage());
        }
    }
    
    private ItemStack createItemFromResultSet(ResultSet rs) throws SQLException {
        Material material = Material.valueOf(rs.getString("material"));
        ItemStack item = new ItemStack(material);
        ItemMeta meta = item.getItemMeta();
        
        // Set display name
        String name = rs.getString("name");
        if (name != null) {
            meta.setDisplayName(name);
        }
        
        // Set lore
        String loreJson = rs.getString("lore");
        if (loreJson != null) {
            List<String> lore = parseJsonList(loreJson);
            meta.setLore(lore);
        }
        
        // Apply enchantments
        String enchantJson = rs.getString("enchantments");
        if (enchantJson != null) {
            applyEnchantments(item, enchantJson);
        }
        
        item.setItemMeta(meta);
        return item;
    }
    
    @Override
    public String prefix() {
        return "db-";
    }
}
```

### NBT-Based Provider

Create items with custom NBT data:

```java
import org.bukkit.NamespacedKey;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;

public class NBTItemProvider implements ItemStackProvider {
    
    private final NamespacedKey customKey;
    private final JavaPlugin plugin;
    
    public NBTItemProvider(JavaPlugin plugin) {
        this.plugin = plugin;
        this.customKey = new NamespacedKey(plugin, "custom_item");
    }
    
    @Override
    public ItemStack provide(String value) throws SerializationException {
        String[] parts = value.split(":");
        if (parts.length != 3) {
            throw new SerializationException(
                "Invalid NBT item format. Expected: material:name:data"
            );
        }
        
        Material material = Material.getMaterial(parts[0].toUpperCase());
        if (material == null) {
            throw new SerializationException("Invalid material: " + parts[0]);
        }
        
        ItemStack item = new ItemStack(material);
        ItemMeta meta = item.getItemMeta();
        
        // Set display name
        meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', parts[1]));
        
        // Add custom NBT data
        PersistentDataContainer container = meta.getPersistentDataContainer();
        container.set(customKey, PersistentDataType.STRING, parts[2]);
        
        item.setItemMeta(meta);
        return item;
    }
    
    @Override
    public String prefix() {
        return "nbt-";
    }
}
```

### API-Based Provider

Integrate with external APIs:

```java
public class APIItemProvider implements ItemStackProvider {
    
    private final String apiUrl;
    private final Gson gson = new Gson();
    
    public APIItemProvider(String apiUrl) {
        this.apiUrl = apiUrl;
    }
    
    @Override
    public ItemStack provide(String value) throws SerializationException {
        try {
            // Make API request
            URL url = new URL(apiUrl + "/items/" + value);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            
            if (conn.getResponseCode() != 200) {
                throw new SerializationException("API returned error: " + conn.getResponseCode());
            }
            
            // Parse JSON response
            BufferedReader reader = new BufferedReader(
                new InputStreamReader(conn.getInputStream())
            );
            ItemData data = gson.fromJson(reader, ItemData.class);
            reader.close();
            
            // Create ItemStack from API data
            return createItemFromData(data);
            
        } catch (IOException e) {
            throw new SerializationException("API error: " + e.getMessage());
        }
    }
    
    private ItemStack createItemFromData(ItemData data) {
        ItemStack item = new ItemStack(Material.valueOf(data.material));
        ItemMeta meta = item.getItemMeta();
        
        if (data.name != null) {
            meta.setDisplayName(data.name);
        }
        
        if (data.lore != null) {
            meta.setLore(data.lore);
        }
        
        if (data.customModelData != null) {
            meta.setCustomModelData(data.customModelData);
        }
        
        item.setItemMeta(meta);
        return item;
    }
    
    @Override
    public String prefix() {
        return "api-";
    }
    
    private static class ItemData {
        String material;
        String name;
        List<String> lore;
        Integer customModelData;
    }
}
```

## Registration

### Basic Registration

Register your provider during plugin initialization:

```java
public class MyPlugin extends JavaPlugin {
    
    @Override
    public void onEnable() {
        // Create and register provider
        CustomItemProvider provider = new CustomItemProvider();
        VMenuAPI.registerItemStackProvider(provider);
        
        getLogger().info("Custom item provider registered!");
    }
}
```

### Registration with Dependencies

Register providers conditionally based on dependencies:

```java
@Override
public void onEnable() {
    // Register database provider if database is available
    if (isDatabaseConfigured()) {
        Connection conn = setupDatabase();
        DatabaseItemProvider dbProvider = new DatabaseItemProvider(conn);
        VMenuAPI.registerItemStackProvider(dbProvider);
    }
    
    // Register API provider if configured
    String apiUrl = getConfig().getString("api.url");
    if (apiUrl != null && !apiUrl.isEmpty()) {
        APIItemProvider apiProvider = new APIItemProvider(apiUrl);
        VMenuAPI.registerItemStackProvider(apiProvider);
    }
}
```

## Advanced Features

### Caching

Implement caching to improve performance:

```java
public class CachedItemProvider implements ItemStackProvider {
    
    private final Map<String, ItemStack> cache = new ConcurrentHashMap<>();
    private final Map<String, Long> cacheTime = new ConcurrentHashMap<>();
    private final long cacheExpiry = 300000; // 5 minutes
    
    @Override
    public ItemStack provide(String value) throws SerializationException {
        // Check if cached item is still valid
        if (cache.containsKey(value)) {
            long cached = cacheTime.getOrDefault(value, 0L);
            if (System.currentTimeMillis() - cached < cacheExpiry) {
                return cache.get(value).clone();
            }
        }
        
        // Load item (expensive operation)
        ItemStack item = loadItem(value);
        
        // Cache the result
        cache.put(value, item);
        cacheTime.put(value, System.currentTimeMillis());
        
        return item.clone();
    }
    
    private ItemStack loadItem(String value) throws SerializationException {
        // Expensive item loading logic
        return new ItemStack(Material.DIAMOND);
    }
    
    @Override
    public String prefix() {
        return "cached-";
    }
}
```

### Dynamic Item Generation

Generate items based on player data:

```java
public class PlayerItemProvider implements ItemStackProvider {
    
    @Override
    public ItemStack provide(String value) throws SerializationException {
        // Value format: "player_head:{uuid}"
        if (value.startsWith("player_head:")) {
            String uuid = value.substring(12);
            return createPlayerHead(uuid);
        }
        
        // Value format: "player_stats:{uuid}"
        if (value.startsWith("player_stats:")) {
            String uuid = value.substring(13);
            return createStatsItem(uuid);
        }
        
        throw new SerializationException("Unknown player item type: " + value);
    }
    
    private ItemStack createPlayerHead(String uuid) {
        ItemStack skull = new ItemStack(Material.PLAYER_HEAD);
        SkullMeta meta = (SkullMeta) skull.getItemMeta();
        
        OfflinePlayer player = Bukkit.getOfflinePlayer(UUID.fromString(uuid));
        meta.setOwningPlayer(player);
        meta.setDisplayName("§6" + player.getName() + "'s Head");
        
        skull.setItemMeta(meta);
        return skull;
    }
    
    private ItemStack createStatsItem(String uuid) {
        OfflinePlayer player = Bukkit.getOfflinePlayer(UUID.fromString(uuid));
        
        ItemStack item = new ItemStack(Material.PAPER);
        ItemMeta meta = item.getItemMeta();
        
        meta.setDisplayName("§6" + player.getName() + "'s Statistics");
        
        List<String> lore = new ArrayList<>();
        lore.add("§7First Joined: §e" + new Date(player.getFirstPlayed()));
        lore.add("§7Last Seen: §e" + new Date(player.getLastPlayed()));
        lore.add("§7Play Time: §e" + formatPlayTime(player));
        
        meta.setLore(lore);
        item.setItemMeta(meta);
        
        return item;
    }
    
    @Override
    public String prefix() {
        return "player-";
    }
}
```

### Validation

Add validation to ensure items are created correctly:

```java
public class ValidatedItemProvider implements ItemStackProvider {
    
    @Override
    public ItemStack provide(String value) throws SerializationException {
        // Validate input format
        if (!isValidFormat(value)) {
            throw new SerializationException(
                "Invalid item format. Expected: type:id:amount"
            );
        }
        
        ItemStack item = createItem(value);
        
        // Validate created item
        if (item == null || item.getType() == Material.AIR) {
            throw new SerializationException("Failed to create valid item");
        }
        
        return item;
    }
    
    private boolean isValidFormat(String value) {
        return value.matches("^[a-zA-Z0-9]+:[a-zA-Z0-9]+:[0-9]+$");
    }
    
    @Override
    public String prefix() {
        return "validated-";
    }
}
```

## Best Practices

### 1. Error Handling

Always provide clear error messages:

```java
@Override
public ItemStack provide(String value) throws SerializationException {
    if (value == null || value.isEmpty()) {
        throw new SerializationException(
            "Item identifier cannot be null or empty"
        );
    }
    
    try {
        return createItem(value);
    } catch (Exception e) {
        throw new SerializationException(
            "Failed to create item '" + value + "': " + e.getMessage()
        );
    }
}
```

### 2. Performance

* Cache frequently used items
* Use async operations for external resources
* Clone items before returning to prevent modification

### 3. Prefix Design

Choose clear, unique prefixes:

* Good: `mysql-`, `redis-`, `custom-`
* Bad: `i-`, `x-`, `1-`

### 4. Documentation

Document your provider's format:

```java
/**
 * Provides items from a MySQL database
 * Format: mysql-{item_id}
 * Example: mysql-legendary_sword
 */
public class MySQLItemProvider implements ItemStackProvider {
    // ...
}
```

## Configuration Usage

Once registered, use your provider in menu configurations:

```yaml
items:
  custom_item:
    slot: 10
    material: "custom-special_sword"
    
  database_item:
    slot: 11
    material: "db-epic_shield"
    
  player_item:
    slot: 12
    material: "player-player_head:%player_uuid%"
    
  api_item:
    slot: 13
    material: "api-seasonal_item_2024"
```

## Complete Example

Here's a complete provider plugin:

```java
package com.example.items;

import com.vulkantechnologies.menu.VMenuAPI;
import com.vulkantechnologies.menu.model.provider.ItemStackProvider;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import org.spongepowered.configurate.serialize.SerializationException;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CustomItemsPlugin extends JavaPlugin {
    
    @Override
    public void onEnable() {
        // Load custom items from config
        ConfigItemProvider provider = new ConfigItemProvider(this);
        provider.loadItems();
        
        // Register the provider
        VMenuAPI.registerItemStackProvider(provider);
        
        getLogger().info("Custom items provider registered!");
    }
    
    public static class ConfigItemProvider implements ItemStackProvider {
        
        private final JavaPlugin plugin;
        private final Map<String, ItemStack> items = new HashMap<>();
        
        public ConfigItemProvider(JavaPlugin plugin) {
            this.plugin = plugin;
        }
        
        public void loadItems() {
            plugin.saveDefaultConfig();
            ConfigurationSection section = plugin.getConfig()
                .getConfigurationSection("custom-items");
            
            if (section == null) return;
            
            for (String key : section.getKeys(false)) {
                ConfigurationSection itemSection = section
                    .getConfigurationSection(key);
                
                Material material = Material.valueOf(
                    itemSection.getString("material", "STONE")
                );
                
                ItemStack item = new ItemStack(material);
                ItemMeta meta = item.getItemMeta();
                
                String name = itemSection.getString("name");
                if (name != null) {
                    meta.setDisplayName(name.replace('&', '§'));
                }
                
                List<String> lore = itemSection.getStringList("lore");
                if (!lore.isEmpty()) {
                    lore.replaceAll(s -> s.replace('&', '§'));
                    meta.setLore(lore);
                }
                
                int modelData = itemSection.getInt("custom-model-data", -1);
                if (modelData >= 0) {
                    meta.setCustomModelData(modelData);
                }
                
                item.setItemMeta(meta);
                items.put(key, item);
            }
            
            plugin.getLogger().info("Loaded " + items.size() + " custom items");
        }
        
        @Override
        public ItemStack provide(String value) throws SerializationException {
            ItemStack item = items.get(value);
            if (item == null) {
                throw new SerializationException(
                    "Custom item not found: " + value
                );
            }
            return item.clone();
        }
        
        @Override
        public String prefix() {
            return "config-";
        }
    }
}
```

Config file (config.yml):

```yaml
custom-items:
  legendary_sword:
    material: NETHERITE_SWORD
    name: "&6Legendary Sword"
    lore:
      - "&7Forged in ancient times"
      - "&7Damage: &c+50"
      - "&7Speed: &a+20%"
    custom-model-data: 1001
    
  mystic_orb:
    material: ENDER_EYE
    name: "&dMystic Orb"
    lore:
      - "&7Contains mysterious power"
      - "&7Right-click to activate"
    custom-model-data: 2001
```
