# Developer API

## Table of Contents

1. [Overview](#overview)
2. [Installation](#installation)
3. [Getting Started](#getting-started)
4. [Core API](#core-api)
5. [Events](#events)
6. [Model Classes](#model-classes)
7. [Feature Management](#feature-management)
8. [Examples](#examples)
9. [Best Practices](#best-practices)
10. [Troubleshooting](#troubleshooting)

## Overview

The OverflowBackpack API provides a comprehensive interface for developers to interact with the OverflowBackpack Minecraft plugin. This API allows external plugins to:

* Manage player backpacks
* Add, remove, and query items
* Handle selling operations
* Configure filters
* Listen to backpack events
* Access backpack statistics
* Control backpack features

### Key Features

* **Asynchronous Operations**: All database operations return CompletableFuture for non-blocking execution
* **Event System**: Comprehensive event system for monitoring and controlling backpack operations
* **Type Safety**: Strongly typed model classes for all data structures
* **Feature Detection**: Check if specific features are enabled before using them
* **Permission Integration**: Built-in permission checking for admin operations
* **Error Handling**: Proper exception handling and error reporting

## Installation

### Maven Setup

Add the following dependency to your `pom.xml`:

```xml
<dependency>
    <groupId>ro.dani3l</groupId>
    <artifactId>overflowbackpack-api</artifactId>
    <version>1.14</version>
    <scope>provided</scope>
</dependency>
```

### Gradle Setup

Add the following to your `build.gradle`:

```gradle
dependencies {
    compileOnly 'ro.dani3l:overflowbackpack-api:1.14'
}
```

### Repository Setup

You may need to add the repository:

```xml
<repository>
    <id>overflowbackpack-repo</id>
    <url>https://repo.hycraft.ro/releases</url>
</repository>
```

## Getting Started

### Basic Plugin Setup

```java
public class MyPlugin extends JavaPlugin {
    private OverflowBackpackAPI backpackAPI;
    
    @Override
    public void onEnable() {
        // Check if OverflowBackpack is available
        try {
            backpackAPI = OverflowBackpackAPI.getInstance();
            if (!backpackAPI.isAvailable()) {
                getLogger().warning("OverflowBackpack plugin not found!");
                getServer().getPluginManager().disablePlugin(this);
                return;
            }
            
            getLogger().info("OverflowBackpack API successfully hooked!");
            getLogger().info("Version: " + backpackAPI.getPluginVersion());
            
            // Register events
            getServer().getPluginManager().registerEvents(new BackpackListener(), this);
            
        } catch (Exception e) {
            getLogger().severe("Failed to hook into OverflowBackpack API: " + e.getMessage());
            getServer().getPluginManager().disablePlugin(this);
        }
    }
    
    @Override
    public void onDisable() {
        // Cleanup if needed
    }
}
```

### Safety Checks

Always perform these checks before using the API:

```java
// Check if API is available
if (backpackAPI == null || !backpackAPI.isAvailable()) {
    // Handle unavailable API
    return;
}

// Check if specific features are enabled
if (!backpackAPI.isFeatureEnabled(BackpackFeature.SELLING_SYSTEM)) {
    player.sendMessage("Selling system is not enabled!");
    return;
}
```

## Core API

### Backpack Management

#### Opening Backpacks

```java
// Open player's own backpack
backpackAPI.openBackpack(player)
    .thenAccept(v -> player.sendMessage("Backpack opened!"))
    .exceptionally(e -> {
        player.sendMessage("Failed to open backpack: " + e.getMessage());
        return null;
    });

// Open with search query
backpackAPI.openBackpack(player, "diamond")
    .thenAccept(v -> player.sendMessage("Backpack opened with search!"));

// Open admin view (requires permission)
backpackAPI.openAdminBackpack(admin, targetPlayer)
    .thenAccept(v -> admin.sendMessage("Admin view opened!"));
```

#### Slot Management

```java
// Get player's slot limit
int slotLimit = backpackAPI.getPlayerSlotLimit(player);
player.sendMessage("Your slot limit: " + slotLimit);

// Check if player reached limit
if (backpackAPI.hasReachedSlotLimit(player)) {
    player.sendMessage("Your backpack is full!");
}

// Get current item count
int itemCount = backpackAPI.getBackpackItemCount(player);
player.sendMessage("Items in backpack: " + itemCount);
```

### Item Operations

#### Adding Items

```java
// Add single item
ItemStack item = new ItemStack(Material.DIAMOND, 1);
backpackAPI.addItem(player, item)
    .thenAccept(success -> {
        if (success) {
            player.sendMessage("Item added!");
        } else {
            player.sendMessage("Failed to add item!");
        }
    });

// Add multiple items
backpackAPI.addItem(player, item, 64)
    .thenAccept(success -> {
        // Handle result
    });
```

#### Removing Items

```java
// Remove by ID
backpackAPI.removeItem(player, itemId)
    .thenAccept(success -> {
        if (success) {
            player.sendMessage("Item removed!");
        }
    });

// Remove by ItemStack
backpackAPI.removeItem(player, itemStack, 32)
    .thenAccept(success -> {
        // Handle result
    });
```

#### Querying Items

```java
// Get all items
backpackAPI.getBackpackItems(player)
    .thenAccept(items -> {
        player.sendMessage("You have " + items.size() + " different items!");
        for (BackpackItem item : items) {
            player.sendMessage("- " + item.getItemName() + " x" + item.getAmount());
        }
    });

// Search items
backpackAPI.searchBackpackItems(player, "sword")
    .thenAccept(items -> {
        player.sendMessage("Found " + items.size() + " swords!");
    });

// Check if item exists
backpackAPI.containsItem(player, itemStack)
    .thenAccept(exists -> {
        if (exists) {
            backpackAPI.getItemAmount(player, itemStack)
                .thenAccept(amount -> {
                    player.sendMessage("You have " + amount + " of this item!");
                });
        }
    });
```

### Selling Operations

```java
// Sell all items
backpackAPI.sellAllItems(player)
    .thenAccept(amount -> {
        if (amount > 0) {
            player.sendMessage("Sold all items for $" + amount);
        } else {
            player.sendMessage("No items to sell!");
        }
    });

// Sell specific item
backpackAPI.sellItem(player, itemId)
    .thenAccept(amount -> {
        player.sendMessage("Sold item for $" + amount);
    });

// Get item price
double price = backpackAPI.getItemSellPrice(player, itemStack);
player.sendMessage("This item sells for $" + price);
```

### Filter Management

```java
// Get filter settings
FilterSettings settings = backpackAPI.getFilterSettings(player);
player.sendMessage("Auto-delete enabled: " + settings.isAutoDeleteEnabled());

// Add item to filter
backpackAPI.addToFilter(player, itemStack, true, false)
    .thenAccept(success -> {
        if (success) {
            player.sendMessage("Item added to filter!");
        }
    });

// Remove from filter
backpackAPI.removeFromFilter(player, itemStack)
    .thenAccept(success -> {
        // Handle result
    });

// Check if item is filtered
backpackAPI.isItemFiltered(player, itemStack)
    .thenAccept(filtered -> {
        if (filtered) {
            player.sendMessage("This item is filtered!");
        }
    });
```

### Statistics

```java
// Get comprehensive statistics
backpackAPI.getBackpackStatistics(player)
    .thenAccept(stats -> {
        player.sendMessage("=== Backpack Statistics ===");
        player.sendMessage("Total Items: " + stats.getTotalItems());
        player.sendMessage("Unique Items: " + stats.getUniqueItems());
        player.sendMessage("Total Value: $" + stats.getTotalValue());
        player.sendMessage("Slots Used: " + stats.getSlotsUsed() + "/" + stats.getSlotsLimit());
    });

// Get total value
backpackAPI.getBackpackTotalValue(player)
    .thenAccept(value -> {
        player.sendMessage("Total backpack value: $" + value);
    });
```

## Events

### Event Types

1. **BackpackItemAddEvent** - Called when an item is added to a backpack
2. **BackpackItemRemoveEvent** - Called when an item is removed from a backpack
3. **BackpackSellEvent** - Called when items are sold from a backpack
4. **BackpackOpenEvent** - Called when a backpack is opened
5. **BackpackCloseEvent** - Called when a backpack is closed
6. **BackpackClearEvent** - Called when a backpack is cleared

### Event Listener Registration

```java
// Method 1: Register with Bukkit
getServer().getPluginManager().registerEvents(new MyListener(), this);

// Method 2: Register with API
backpackAPI.registerEventListener(new MyListener());
```

### Event Handling Examples

```java
public class MyListener implements Listener {
    
    @EventHandler
    public void onBackpackItemAdd(BackpackItemAddEvent event) {
        Player player = event.getPlayer();
        ItemStack item = event.getItemStack();
        
        // Log the addition
        getLogger().info(player.getName() + " added " + event.getAmount() + 
                         "x " + item.getType().name() + " to backpack");
        
        // Cancel certain items
        if (item.getType() == Material.BEDROCK) {
            event.setCancelled(true);
            event.setCancelReason("Bedrock cannot be stored!");
        }
        
        // Apply effects for rare items
        if (isRareItem(item)) {
            player.sendMessage("✨ Rare item added!");
            player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 1);
        }
    }
    
    @EventHandler
    public void onBackpackSell(BackpackSellEvent event) {
        Player player = event.getPlayer();
        double amount = event.getAmountEarned();
        
        // Apply bonus for large sales
        if (amount >= 1000) {
            double bonus = amount * 0.1;
            player.sendMessage("Bonus: +" + bonus + " for large sale!");
            // Apply bonus through economy
        }
    }
}
```

## Model Classes

### BackpackItem

Represents an item stored in a backpack:

```java
BackpackItem item = ...;
long id = item.getId();                    // Unique ID
String playerUuid = item.getPlayerUuid();  // Owner UUID
String itemType = item.getItemType();       // Material type
String itemName = item.getItemName();       // Display name
String itemData = item.getItemData();       // Serialized data
int amount = item.getAmount();              // Amount
Date createdAt = item.getCreatedAt();       // Creation date
Date deletedAt = item.getDeletedAt();       // Deletion date (null if active)
Date soldAt = item.getSoldAt();             // Sale date (null if not sold)
double soldFor = item.getSoldFor();          // Sale price (0 if not sold)

// Convenience methods
boolean isActive = item.isActive();          // Not deleted and not sold
boolean isDeleted = item.isDeleted();       // Has been deleted
boolean isSold = item.isSold();             // Has been sold
ItemStack stack = item.getItemStack();      // Get as ItemStack
```

### BackpackStatistics

Contains backpack statistics:

```java
BackpackStatistics stats = ...;
int totalItems = stats.getTotalItems();     // Total item count
int uniqueItems = stats.getUniqueItems();    // Unique item types
double totalValue = stats.getTotalValue();   // Total monetary value
int slotsUsed = stats.getSlotsUsed();        // Slots currently used
int slotsLimit = stats.getSlotsLimit();      // Maximum slots allowed
Date lastAccess = stats.getLastAccess();    // Last access time
```

### FilterSettings

Represents filter configuration:

```java
FilterSettings settings = ...;
boolean autoDelete = settings.isAutoDeleteEnabled();    // Auto-delete filtered items
boolean notifications = settings.isNotificationsEnabled(); // Show notifications
List<String> filtered = settings.getFilteredItems();   // List of filtered item types

// Create new settings
FilterSettings newSettings = FilterSettings.builder()
    .autoDeleteEnabled(true)
    .notificationsEnabled(false)
    .filteredItems(Arrays.asList("DIAMOND", "EMERALD"))
    .build();
```

## Feature Management

### Available Features

```java
// Check various features
boolean slotSystem = backpackAPI.isFeatureEnabled(BackpackFeature.SLOT_SYSTEM);
boolean selling = backpackAPI.isFeatureEnabled(BackpackFeature.SELLING_SYSTEM);
boolean economy = backpackAPI.isFeatureEnabled(BackpackFeature.ECONOMY_INTEGRATION);
boolean worldguard = backpackAPI.isFeatureEnabled(BackpackFeature.WORLDGUARD_INTEGRATION);
boolean mcmmo = backpackAPI.isFeatureEnabled(BackpackFeature.MCMMO_INTEGRATION);
```

### Feature List

* `SLOT_SYSTEM` - Player slot limits based on permissions
* `ACTIONBAR_NOTIFICATIONS` - Actionbar notifications for item additions
* `FILTER_SYSTEM` - Item filtering functionality
* `AUTO_DELETION` - Automatic deletion of filtered items
* `FILTER_NOTIFICATIONS` - Notifications for filtered items
* `SELLING_SYSTEM` - Item selling functionality
* `ADMIN_COMMANDS` - Admin-only commands
* `SEARCH_FEATURE` - Backpack search functionality
* `INFINITE_SLOTS` - Unlimited storage slots
* `ECONOMY_INTEGRATION` - Economy plugin integration
* `WORLDGUARD_INTEGRATION` - WorldGuard plugin integration
* `MCMMO_INTEGRATION` - mcMMO plugin integration
* `ROSESTACKER_INTEGRATION` - RoseStacker plugin integration
* `WILDSTACKER_INTEGRATION` - WildStacker plugin integration
* `ORAXEN_INTEGRATION` - Oraxen plugin integration
* `BLOCK_COMPRESSOR` - Block compression functionality
* `ITEM_WHITELIST` - Item whitelisting
* `COOLDOWN_SYSTEM` - Action cooldowns
* `GUI_OPEN_SOUND` - Sound on opening the main GUI
* `ACTION_SOUND_ON_CLAIM` - Sound on claiming items
* `ACTION_SOUND_ON_DELETE` - Sound on deleting items
* `ACTION_SOUND_ON_SELL` - Sound on selling items
* `SELLALL_GUI_OPEN_SOUND` - Sound on opening the Sell-All GUI
* `PAGE_PREVIOUS_CLICK_SOUND` - Sound on previous page navigation click
* `PAGE_NEXT_CLICK_SOUND` - Sound on next page navigation click
* `SEARCH_CLICK_SOUND` - Sound when clicking search
* `AUTO_DELETION_SOUND` - Sound for auto-deletion actionbar notification
* `BLOCK_COMPRESSOR_SOUND` - Sound used by Block Compressor actions

## Examples

### Complete Example Plugin

See the `ExamplePlugin.java` file in the API package for a complete example plugin that demonstrates:

* API initialization and safety checks
* Command handling for various operations
* Event listening and handling
* Error handling and user feedback
* Permission checking
* Feature detection

### Common Use Cases

#### 1. Custom Backpack Command

```java
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
    if (!(sender instanceof Player)) return true;
    Player player = (Player) sender;
    
    if (cmd.getName().equalsIgnoreCase("mybackpack")) {
        if (backpackAPI == null || !backpackAPI.isAvailable()) {
            player.sendMessage("§cBackpack system not available!");
            return true;
        }
        
        backpackAPI.openBackpack(player)
            .thenAccept(v -> player.sendMessage("§aBackpack opened!"))
            .exceptionally(e -> {
                player.sendMessage("§cFailed to open backpack!");
                return null;
            });
    }
    return true;
}
```

#### 2. Item Reward System

```java
public void giveReward(Player player, ItemStack reward) {
    if (backpackAPI != null && backpackAPI.isAvailable()) {
        backpackAPI.addItem(player, reward)
            .thenAccept(success -> {
                if (success) {
                    player.sendMessage("§aReward added to your backpack!");
                } else {
                    // Fallback: give to inventory
                    player.getInventory().addItem(reward);
                    player.sendMessage("§eReward added to inventory (backpack full)!");
                }
            });
    } else {
        // Fallback: give to inventory
        player.getInventory().addItem(reward);
    }
}
```

#### 3. Economy Integration

```java
@EventHandler
public void onBackpackSell(BackpackSellEvent event) {
    Player player = event.getPlayer();
    double amount = event.getAmountEarned();
    
    // Apply VIP bonus
    if (player.hasPermission("vip.backpack")) {
        double bonus = amount * 0.2; // 20% bonus for VIPs
        // Give bonus through economy
        economy.depositPlayer(player, bonus);
        player.sendMessage("§6VIP Bonus: +" + String.format("%.2f", bonus));
    }
}
```

#### 4. Statistics Tracking

```java
public void updateLeaderboard() {
    for (Player player : getServer().getOnlinePlayers()) {
        if (backpackAPI != null && backpackAPI.isAvailable()) {
            backpackAPI.getBackpackStatistics(player)
                .thenAccept(stats -> {
                    // Update leaderboard with stats
                    leaderboardManager.updatePlayerStats(player.getUniqueId(), stats);
                });
        }
    }
}
```

## Best Practices

### 1. API Safety

```java
// Always check API availability
if (backpackAPI == null || !backpackAPI.isAvailable()) {
    // Handle unavailable API
    return;
}

// Check features before using them
if (!backpackAPI.isFeatureEnabled(BackpackFeature.SELLING_SYSTEM)) {
    player.sendMessage("Selling is not enabled!");
    return;
}
```

### 2. Async Operations

```java
// Always handle CompletableFuture properly
backpackAPI.addItem(player, item)
    .thenAccept(success -> {
        // Handle success on main thread
        Bukkit.getScheduler().runTask(plugin, () -> {
            if (success) {
                player.sendMessage("§aItem added!");
            } else {
                player.sendMessage("§cFailed to add item!");
            }
        });
    })
    .exceptionally(throwable -> {
        // Handle errors
        getLogger().warning("Error adding item: " + throwable.getMessage());
        return null;
    });
```

### 3. Event Handling

```java
// Use appropriate event priorities
@EventHandler(priority = EventPriority.HIGH)
public void onItemAdd(BackpackItemAddEvent event) {
    // High priority for cancellation logic
}

@EventHandler(priority = EventPriority.MONITOR)
public void onItemAddMonitor(BackpackItemAddEvent event) {
    // Monitor priority for logging (runs after all other handlers)
}

// Always check if event is cancelled
@EventHandler
public void onItemAdd(BackpackItemAddEvent event) {
    if (event.isCancelled()) {
        return; // Don't process cancelled events
    }
    // Your logic here
}
```

### 4. Permission Checking

```java
public void handleAdminCommand(Player player, OfflinePlayer target) {
    if (!player.hasPermission("myplugin.backpack.admin")) {
        player.sendMessage("§cYou don't have permission for this command!");
        return;
    }
    
    backpackAPI.openAdminBackpack(player, target)
        .thenAccept(v -> player.sendMessage("§aOpened " + target.getName() + "'s backpack!"));
}
```

### 5. Error Handling

```java
public void safeOperation(Player player) {
    try {
        backpackAPI.getBackpackItems(player)
            .thenAccept(items -> {
                // Process items
            })
            .exceptionally(throwable -> {
                // Log error
                getLogger().severe("Error getting backpack items: " + throwable.getMessage());
                // Notify player
                player.sendMessage("§cAn error occurred while accessing your backpack.");
                return null;
            });
    } catch (Exception e) {
        getLogger().severe("Unexpected error: " + e.getMessage());
        player.sendMessage("§cAn unexpected error occurred.");
    }
}
```

### 6. Resource Management

```java
@Override
public void onDisable() {
    // Unregister event listeners
    if (backpackAPI != null) {
        backpackAPI.unregisterEventListener(myListener);
    }
    
    // Cancel any pending tasks
    Bukkit.getScheduler().cancelTasks(this);
}
```

## Troubleshooting

### Common Issues

#### 1. API Not Available

**Problem**: `OverflowBackpackAPI.getInstance()` throws exception or returns null

**Solution**:

* Ensure OverflowBackpack plugin is installed and enabled
* Check plugin load order (make your plugin depend on OverflowBackpack)
* Use proper dependency loading in `plugin.yml`

```yaml
depend: [OverflowBackpack]
```

#### 2. Feature Not Enabled

**Problem**: Operations fail because features are disabled

**Solution**: Check feature availability before use

```java
if (!backpackAPI.isFeatureEnabled(BackpackFeature.SELLING_SYSTEM)) {
    player.sendMessage("Selling system is disabled!");
    return;
}
```

#### 3. Permission Issues

**Problem**: Admin operations fail

**Solution**: Ensure proper permissions are set

```java
if (!player.hasPermission("overflowbackpack.admin")) {
    player.sendMessage("§cAdmin permission required!");
    return;
}
```

#### 4. Async Operation Issues

**Problem**: Operations fail or cause threading issues

**Solution**: Handle async operations properly

```java
backpackAPI.addItem(player, item)
    .thenAccept(success -> {
        // Run Bukkit operations on main thread
        Bukkit.getScheduler().runTask(plugin, () -> {
            player.sendMessage("Item added: " + success);
        });
    });
```

#### 5. Event Not Firing

**Problem**: Event listeners not being called

**Solution**:

* Ensure listener is registered
* Check event priority
* Verify event is not being cancelled

```java
// Register listener
getServer().getPluginManager().registerEvents(this, this);

// Or register with API
backpackAPI.registerEventListener(this);
```

### Debugging Tips

1. **Enable Debug Logging**: Set your plugin to debug mode to see detailed logs
2. **Check API Version**: Ensure you're using the correct API version
3. **Verify Dependencies**: Make sure all required plugins are loaded
4. **Test Permissions**: Verify players have required permissions
5. **Monitor Console**: Watch for error messages in server console

### Getting Help

* **Discord**: Join the community Discord for support
* **Examples**: Review the example plugins for reference

***

### Version History

* **1.14**: Initial API release with full backpack management functionality
* Future versions will maintain backward compatibility where possible

## Hooking the API from another plugin (initialization guide)

If you are integrating OverflowBackpack from another plugin and still see errors like:

```
[Guilders] Failed to hook into OverflowBackpack API: This method should be implemented by the main plugin
```

or

```
OverflowBackpack API is not available. Is the plugin loaded?
```

follow this guide to initialize the API correctly.

### 1) Add the API as a compile-only dependency (do NOT shade it)

Use one of the setups from the Installation section, but ensure the scope is compileOnly/provided and that you do NOT relocate/shade the API into your own plugin JAR. Shading the API produces a separate copy of `OverflowBackpackAPIProvider`, which prevents hooking.

* Maven (recommended):

```xml
<dependency>
    <groupId>ro.dani3l</groupId>
    <artifactId>overflowbackpack-api</artifactId>
    <version>1.14</version>
    <scope>provided</scope>
</dependency>
```

* Gradle:

```gradle
dependencies {
    compileOnly 'ro.dani3l:overflowbackpack-api:1.14'
}
```

### 2) Declare proper load order in plugin.yml

* If your plugin requires OverflowBackpack to be present, use `depend` (guarantees load order):

```yaml
depend: [OverflowBackpack]
```

* If OverflowBackpack is optional, use `softdepend` and hook safely at runtime:

```yaml
softdepend: [OverflowBackpack]
```

### 3) Safe initialization patterns

Pick one of the following patterns depending on whether the dependency is mandatory or optional.

* Option A — Mandatory (using `depend`): call in `onEnable()`

```java
public final class MyPlugin extends JavaPlugin {
  private OverflowBackpackAPI api;

  @Override
  public void onEnable() {
    try {
      this.api = OverflowBackpackAPI.getInstance();
      getLogger().info("Hooked OverflowBackpack v" + api.getPluginVersion());
    } catch (IllegalStateException ex) {
      // Should not happen with 'depend', but guard just in case
      getLogger().severe("Failed to hook OverflowBackpack: " + ex.getMessage());
      getServer().getPluginManager().disablePlugin(this);
      return;
    }

    // register listeners, commands, etc.
  }
}
```

* Option B — Optional (using `softdepend`): wait for the plugin to be enabled

```java
public final class MyPlugin extends JavaPlugin implements Listener {
  private OverflowBackpackAPI api;

  @Override
  public void onEnable() {
    // 1) If already enabled, hook immediately
    if (getServer().getPluginManager().isPluginEnabled("OverflowBackpack")) {
      hookOverflowBackpack();
    } else {
      // 2) Otherwise, listen for when it enables
      getServer().getPluginManager().registerEvents(this, this);
    }
  }

  @EventHandler
  public void onPluginEnable(org.bukkit.event.server.PluginEnableEvent event) {
    if ("OverflowBackpack".equalsIgnoreCase(event.getPlugin().getName())) {
      hookOverflowBackpack();
      org.bukkit.event.HandlerList.unregisterAll(this);
    }
  }

  private void hookOverflowBackpack() {
    try {
      // If you still catch IllegalStateException here, schedule for next tick
      this.api = OverflowBackpackAPI.getInstance();
      getLogger().info("Hooked OverflowBackpack v" + api.getPluginVersion());
    } catch (IllegalStateException ex) {
      // On some servers, enabling completes at end of tick — defer by one tick
      getServer().getScheduler().runTask(this, () -> {
        this.api = OverflowBackpackAPI.getInstance();
        getLogger().info("Hooked OverflowBackpack v" + api.getPluginVersion());
      });
    }
  }
}
```

### 4) Verify the hook

* On server start, the OverflowBackpack console should print: `OverflowBackpack API registered`.
* In your plugin, log the version after hooking: `Hooked OverflowBackpack vX.Y.Z`.
* Make sure there is only one OverflowBackpack JAR in the server `plugins/` folder and it is v1.14+.

### 5) About the legacy error message

* The message `This method should be implemented by the main plugin` belonged to older API behavior (or when calling certain model helpers before the main plugin completed registration). In v1.14+, `OverflowBackpackAPI.getInstance()` throws `IllegalStateException` with a clear message if you call it too early.
* If you still see the legacy message:
  * Ensure your server uses OverflowBackpack v1.14+ (not an older JAR).
  * Ensure you are not shading the API into your plugin.
  * Ensure you obtain the API instance only after OverflowBackpack has enabled (see patterns above).

### 6) Note about `BackpackItem#getItemStack()`

As of v1.14, the main plugin registers an internal deserializer bridge during its `onEnable()`. That means `BackpackItem#getItemStack()` will only work after OverflowBackpack has fully enabled. Always hook the API first (as shown above) before using model convenience methods. If called too early, you may get null or a legacy error from older builds.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dani3l.gitbook.io/plugins/overflow-backpack/developer-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
