Skip to content

Container API

Containers are objects that hold items in Allay. This includes player inventories, chests, furnaces, and many other blocks that can store items. Allay also provides a powerful "fake container" system for creating custom GUIs.

Key Concepts

Container

Container is the base interface for all containers. It provides methods for managing items and viewers.

Key Methods:

Method Description
getContainerType() Get the container type
getItemStack(int slot) Get the item at a specific slot
setItemStack(int slot, item) Set an item at a specific slot
clearSlot(int slot) Clear a specific slot
clearAllSlots() Clear all slots
isEmpty() Check if the container is empty
tryAddItem(ItemStack) Try to add an item, merging with existing stacks
addViewer(ContainerViewer) Add a viewer to the container
removeViewer(ContainerViewer) Remove a viewer from the container
getViewers() Get all current viewers

ContainerType

ContainerType represents the type of a container and defines its size. Container types are used to identify and retrieve containers from a ContainerHolder.

ContainerTypes

ContainerTypes contains all available container types:

Type Size Description
INVENTORY 36 Player inventory
ARMOR 4 Player armor slots
OFFHAND 1 Player offhand slot
CHEST 27 Single chest
DOUBLE_CHEST 54 Double chest
BARREL 27 Barrel
SHULKER_BOX 27 Shulker box
ENDER_CHEST 27 Ender chest
FURNACE 3 Furnace
BLAST_FURNACE 3 Blast furnace
SMOKER 3 Smoker
BREWING_STAND 5 Brewing stand
ANVIL 3 Anvil
ENCHANT_TABLE 2 Enchanting table
CRAFTING_TABLE 9 Crafting table
CRAFTING_GRID 4 Player 2x2 crafting grid
STONECUTTER 2 Stonecutter
GRINDSTONE 3 Grindstone
LOOM 4 Loom
SMITHING_TABLE 4 Smithing table
CARTOGRAPHY_TABLE 3 Cartography table
BEACON 1 Beacon
FAKE_CHEST 27 Fake chest (virtual container)
FAKE_DOUBLE_CHEST 54 Fake double chest (virtual)
... and more

ContainerHolder

ContainerHolder is an interface for objects that hold multiple containers. EntityPlayer implements this interface to hold inventory, armor, and offhand containers.

import org.allaymc.api.container.ContainerTypes;
import org.allaymc.api.entity.interfaces.EntityPlayer;

public void accessPlayerContainers(EntityPlayer player) {
    // Get player's inventory container
    var inventory = player.getContainer(ContainerTypes.INVENTORY);

    // Get player's armor container
    var armor = player.getContainer(ContainerTypes.ARMOR);

    // Get player's offhand container
    var offhand = player.getContainer(ContainerTypes.OFFHAND);
}

ContainerViewer

ContainerViewer is an interface for entities that can view and interact with containers. Player implements this interface.

Working with Player Inventory

Getting Items

import org.allaymc.api.container.ContainerTypes;
import org.allaymc.api.entity.interfaces.EntityPlayer;
import org.allaymc.api.item.ItemStack;

public void getPlayerItems(EntityPlayer player) {
    // Get the item in the player's hand
    ItemStack itemInHand = player.getItemInHand();

    // Get the current hand slot index (0-8)
    int handSlot = player.getHandSlot();

    // Get an item from a specific inventory slot
    var inventory = player.getContainer(ContainerTypes.INVENTORY);
    ItemStack item = inventory.getItemStack(0);

    // Get all items in the inventory
    var allItems = inventory.getItemStacks();
}

Setting Items

import org.allaymc.api.container.ContainerTypes;
import org.allaymc.api.entity.interfaces.EntityPlayer;
import org.allaymc.api.item.type.ItemTypes;

public void setPlayerItems(EntityPlayer player) {
    // Set item in hand
    player.setItemInHand(ItemTypes.DIAMOND_SWORD.createItemStack());

    // Clear item in hand
    player.clearItemInHand();

    // Set item at a specific slot
    var inventory = player.getContainer(ContainerTypes.INVENTORY);
    inventory.setItemStack(0, ItemTypes.DIAMOND.createItemStack(64));

    // Clear a specific slot
    inventory.clearSlot(0);

    // Clear the entire inventory
    inventory.clearAllSlots();
}

Adding Items

import org.allaymc.api.entity.interfaces.EntityPlayer;
import org.allaymc.api.item.type.ItemTypes;

public void addItemsToPlayer(EntityPlayer player) {
    var diamond = ItemTypes.DIAMOND.createItemStack(32);

    // Try to add item - will merge with existing stacks or find empty slot
    // If inventory is full, the item will be dropped at the player's position
    player.tryAddItem(diamond);
}

Working with Block Containers

Block containers (chests, furnaces, etc.) can be accessed when players interact with them:

import org.allaymc.api.container.ContainerTypes;
import org.allaymc.api.entity.interfaces.EntityPlayer;
import org.allaymc.api.item.type.ItemTypes;

public void workWithBlockContainer(EntityPlayer player) {
    // Get the container the player has opened (e.g., a chest)
    var controller = player.getController();
    if (controller != null) {
        var openedChest = controller.getOpenedContainer(ContainerTypes.CHEST);
        if (openedChest != null) {
            // Add an item to the chest
            openedChest.tryAddItem(ItemTypes.GOLD_INGOT.createItemStack(16));

            // Get an item from the chest
            var item = openedChest.getItemStack(0);
        }
    }
}

Container Listeners

Containers support various listeners for monitoring changes:

import org.allaymc.api.container.Container;
import org.allaymc.api.container.ContainerViewer;

public void setupContainerListeners(Container container) {
    // Listen for container open events
    container.addOpenListener((ContainerViewer viewer) -> {
        System.out.println("Container opened by a viewer");
    });

    // Listen for container close events
    container.addCloseListener((ContainerViewer viewer) -> {
        System.out.println("Container closed by a viewer");
    });

    // Listen for changes to a specific slot
    container.addSlotChangeListener(0, (itemStack) -> {
        System.out.println("Slot 0 changed to: " + itemStack.getItemType().getIdentifier());
    });
}

Fake Containers (Virtual GUIs)

Fake containers are virtual containers that don't correspond to any real block in the world. They are useful for creating custom GUIs like menus, shops, or interactive interfaces.

Creating Fake Containers

Use FakeContainerFactory to create fake containers:

1
2
3
4
5
6
7
8
import org.allaymc.api.container.FakeContainerFactory;
import org.allaymc.api.container.interfaces.FakeContainer;

// Create a fake chest (27 slots)
FakeContainer fakeChest = FakeContainerFactory.getFactory().createFakeChestContainer();

// Create a fake double chest (54 slots)
FakeContainer fakeDoubleChest = FakeContainerFactory.getFactory().createFakeDoubleChestContainer();

Configuring Fake Containers

import org.allaymc.api.container.FakeContainerFactory;
import org.allaymc.api.item.type.ItemTypes;

public void configureFakeContainer() {
    var fakeChest = FakeContainerFactory.getFactory().createFakeChestContainer();

    // Set a custom name (displayed in the container header)
    fakeChest.setCustomName("My Custom Menu");

    // Add items to the container
    fakeChest.setItemStack(0, ItemTypes.DIAMOND.createItemStack());
    fakeChest.setItemStack(4, ItemTypes.EMERALD.createItemStack());
    fakeChest.setItemStack(8, ItemTypes.GOLD_INGOT.createItemStack());
}

Adding Click Listeners

Fake containers support click listeners that are triggered when players interact with items:

import org.allaymc.api.container.FakeContainerFactory;
import org.allaymc.api.item.type.ItemTypes;
import org.allaymc.api.player.Player;

public void createInteractiveMenu(Player player) {
    var menu = FakeContainerFactory.getFactory().createFakeChestContainer();
    menu.setCustomName("Shop Menu");

    // Add item with click listener
    menu.setItemStackWithListener(0, ItemTypes.DIAMOND_SWORD.createItemStack(), () -> {
        player.sendMessage("You clicked on the Diamond Sword!");
        // Add your shop logic here
    });

    // Or add listener separately
    menu.setItemStack(4, ItemTypes.GOLDEN_APPLE.createItemStack());
    menu.addClickListener(4, () -> {
        player.sendMessage("You clicked on the Golden Apple!");
    });

    // Show the menu to the player
    menu.addPlayer(player);
}

Showing Fake Containers to Players

Important

Use addPlayer() instead of addViewer() for fake containers. The addPlayer() method handles sending fake blocks to the client before opening the container.

import org.allaymc.api.container.FakeContainerFactory;
import org.allaymc.api.player.Player;

public void showFakeContainer(Player player) {
    var fakeChest = FakeContainerFactory.getFactory().createFakeChestContainer();
    fakeChest.setCustomName("My Menu");

    // Show to player (with optional callback)
    fakeChest.addPlayer(player, success -> {
        if (success) {
            System.out.println("Container opened successfully");
        } else {
            System.out.println("Failed to open container");
        }
    });

    // Or without callback
    fakeChest.addPlayer(player);
}

Closing Fake Containers

import org.allaymc.api.container.interfaces.FakeContainer;
import org.allaymc.api.player.Player;

public void closeFakeContainer(FakeContainer container, Player player) {
    // Remove a specific player
    container.removeViewer(player);

    // Remove all viewers
    container.removeAllViewers();
}

Practical Examples

Simple Shop Menu

import org.allaymc.api.container.FakeContainerFactory;
import org.allaymc.api.container.interfaces.FakeContainer;
import org.allaymc.api.item.type.ItemTypes;
import org.allaymc.api.player.Player;

public class ShopMenu {
    private final FakeContainer menu;
    private final Player player;

    public ShopMenu(Player player) {
        this.player = player;
        this.menu = FakeContainerFactory.getFactory().createFakeChestContainer();
        setupMenu();
    }

    private void setupMenu() {
        menu.setCustomName("Server Shop");

        // Diamond Sword - 10 emeralds
        menu.setItemStackWithListener(10, ItemTypes.DIAMOND_SWORD.createItemStack(), () -> {
            buyItem("Diamond Sword", 10);
        });

        // Golden Apple - 5 emeralds
        menu.setItemStackWithListener(12, ItemTypes.GOLDEN_APPLE.createItemStack(), () -> {
            buyItem("Golden Apple", 5);
        });

        // Diamond Armor Set - 40 emeralds
        menu.setItemStackWithListener(14, ItemTypes.DIAMOND_CHESTPLATE.createItemStack(), () -> {
            buyItem("Diamond Armor Set", 40);
        });

        // Close button
        menu.setItemStackWithListener(26, ItemTypes.BARRIER.createItemStack(), () -> {
            close();
        });
    }

    private void buyItem(String itemName, int price) {
        player.sendMessage("You purchased " + itemName + " for " + price + " emeralds!");
        // Add your purchase logic here
    }

    public void open() {
        menu.addPlayer(player);
    }

    public void close() {
        menu.removeViewer(player);
    }
}

Paginated Menu

import org.allaymc.api.container.FakeContainerFactory;
import org.allaymc.api.container.interfaces.FakeContainer;
import org.allaymc.api.item.ItemStack;
import org.allaymc.api.item.type.ItemTypes;
import org.allaymc.api.player.Player;

import java.util.List;

public class PaginatedMenu {
    private final Player player;
    private final List<ItemStack> items;
    private final int itemsPerPage = 45; // Leave bottom row for navigation
    private int currentPage = 0;
    private FakeContainer menu;

    public PaginatedMenu(Player player, List<ItemStack> items) {
        this.player = player;
        this.items = items;
    }

    public void open() {
        showPage(0);
    }

    private void showPage(int page) {
        this.currentPage = page;

        // Close previous menu if open
        if (menu != null) {
            menu.removeViewer(player);
        }

        menu = FakeContainerFactory.getFactory().createFakeDoubleChestContainer();
        menu.setCustomName("Items - Page " + (page + 1));

        // Calculate items for this page
        int startIndex = page * itemsPerPage;
        int endIndex = Math.min(startIndex + itemsPerPage, items.size());

        // Add items
        for (int i = startIndex; i < endIndex; i++) {
            int slot = i - startIndex;
            ItemStack item = items.get(i);
            menu.setItemStackWithListener(slot, item, () -> {
                player.sendMessage("You selected: " + item.getItemType().getIdentifier());
            });
        }

        // Previous page button (slot 45)
        if (page > 0) {
            menu.setItemStackWithListener(45, ItemTypes.ARROW.createItemStack(), () -> {
                showPage(currentPage - 1);
            });
        }

        // Next page button (slot 53)
        if (endIndex < items.size()) {
            menu.setItemStackWithListener(53, ItemTypes.ARROW.createItemStack(), () -> {
                showPage(currentPage + 1);
            });
        }

        // Close button (slot 49)
        menu.setItemStackWithListener(49, ItemTypes.BARRIER.createItemStack(), () -> {
            menu.removeViewer(player);
        });

        menu.addPlayer(player);
    }

    public int getTotalPages() {
        return (int) Math.ceil((double) items.size() / itemsPerPage);
    }
}

Confirmation Dialog

import org.allaymc.api.container.FakeContainerFactory;
import org.allaymc.api.container.interfaces.FakeContainer;
import org.allaymc.api.item.type.ItemTypes;
import org.allaymc.api.player.Player;

public class ConfirmationDialog {

    public static void show(Player player, String title, Runnable onConfirm, Runnable onCancel) {
        var dialog = FakeContainerFactory.getFactory().createFakeChestContainer();
        dialog.setCustomName(title);

        // Confirm button (green wool)
        dialog.setItemStackWithListener(11, ItemTypes.GREEN_WOOL.createItemStack(), () -> {
            dialog.removeViewer(player);
            onConfirm.run();
        });

        // Cancel button (red wool)
        dialog.setItemStackWithListener(15, ItemTypes.RED_WOOL.createItemStack(), () -> {
            dialog.removeViewer(player);
            onCancel.run();
        });

        dialog.addPlayer(player);
    }
}

// Usage:
// ConfirmationDialog.show(player, "Delete all items?",
//     () -> player.sendMessage("Confirmed!"),
//     () -> player.sendMessage("Cancelled!")
// );

Tips

Fake Container Lifecycle

Remember to remove viewers from fake containers when they are no longer needed. Fake containers send fake blocks to the client, which are cleaned up when the viewer is removed.

Click Listener Cleanup

If you're reusing fake containers, call removeAllClickListeners() before setting up new listeners to avoid duplicate callbacks.

Player.getController()

When working with fake containers, use player.getController() to get the Player interface from an EntityPlayer. The controller handles the network communication for containers.

Container Updates

When you modify items in a container using setItemStack(), viewers are automatically notified. If you modify an ItemStack directly (e.g., changing count), call notifySlotChange(slot) to update viewers.

Comments