Java server development on Paper · 3 min read

Paper menus: prevent item transfers and close windows safely

To prevent a button item from being taken out, the GUI needs to be protected by the inventory operation, not by the item name. Let's create a small information window: all transfers are prohibited, the 4 slot responds only to left clicks.

LonelyCraft editors · Updated

Desktop with computer and world Minecraft on screen
Illustration: desktop with a computer and world Minecraft on the screen. This is an artistic scene, not a snapshot of a specific server.

Recognize your window by its owner

On Paper 26.3 and Java 25 use TrailNotes project. The main class below registers /safemenu itself; manifest remains basic. The native InventoryHolder binds the server inventory to the Menu object. The title may be the same as another window, so it is not suitable for testing.

The rule in the example is intentionally strict: while the window is open, operations in both the top and bottom inventory are prohibited. This is an information screen, not a ready-made store.

Matrix of ways to move objects and safe GUI handlers
Locking affects the entire open window, button recognition only affects a specific raw slot.Scroll the diagram horizontally.Open full-size diagram

Handle click and drag separately

Replace the main Java file with this class. Any click in our window is first canceled; Only then is the button recognized. Closing is moved to the next tick according to the InventoryClickEvent contract. The same top inventory object is checked before closing.

package ru.example.trailnotes;

import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;

public final class TrailNotesPlugin extends JavaPlugin implements Listener {
    private static final class Menu implements InventoryHolder {
        private final Inventory inventory = Bukkit.createInventory(this, 9, net.kyori.adventure.text.Component.text("Безопасное меню"));
        @Override public Inventory getInventory() { return inventory; }
    }
    @Override public void onEnable() {
        getServer().getPluginManager().registerEvents(this, this);
        registerCommand("safemenu", (source, args) -> openMenu(source.getSender()));
    }
    private void openMenu(CommandSender sender) {
        if (!(sender instanceof Player player)) {
            sender.sendMessage("Меню открывается игроку.");
            return;
        }
        Menu menu = new Menu();
        menu.inventory.setItem(4, new ItemStack(Material.PAPER));
        player.openInventory(menu.inventory);
        return;
    }
    @EventHandler public void click(InventoryClickEvent event) {
        Inventory top = event.getView().getTopInventory();
        if (!(top.getHolder() instanceof Menu)) return;
        event.setCancelled(true);
        if (event.getRawSlot() != 4 || event.getClick() != ClickType.LEFT) return;
        Player player = (Player) event.getWhoClicked();
        getServer().getScheduler().runTask(this, () -> {
            if (!player.isOnline() || player.getOpenInventory().getTopInventory() != top) return;
            player.closeInventory();
            player.sendMessage("Кнопка сработала; предмет остался в меню.");
        });
    }
    @EventHandler public void drag(InventoryDragEvent event) {
        if (event.getView().getTopInventory().getHolder() instanceof Menu) {
            event.setCancelled(true);
        }
    }
}

Try to take the button item out in different ways

Install the JAR into the test Paper. Place a regular item in the hotbar, open /safemenu and follow all the steps below. After closing and re-entering, the number of items should remain the same.

ActionWaiting
Left click in slot4The window is closed, PAPER is not issued
Shift-click from lower inventoryNo transfer
Hotbar number or F above the buttonNo exchange
Double click with an item on the cursorNo button collection
Drag through top slotsNo layout

Don't confuse window protection with payment

Two clicks before the next tick can deliver two callbacks. The first one will close the window; the second one will see another top inventory and exit. For the background, additionally store the UUID and opening number - the same window should not accept the result of the old request.

If you add a purchase, check the permission, current price and full-inventory policy before withdrawing money. Durable transaction records and retry-safe delivery are separate concerns. Blocking shift-click does not make a payment execute only once.

The request number filters the result after exiting or changing the screen. Don't apply answer to old window.

The replay check must survive a server restart. Save the fact of purchase.

Frequently asked questions

Why are clicks on the bottom inventory cancelled?

Shift-click and collecting matching items can affect the top slots. The example blocks all item transfers while its GUI is open.

Drag will go through InventoryClickEvent?

No. Dragging requires a separate InventoryDragEvent.

Why recheck top inventory?

Until the next tick, the player could close the screen or open another one. Callback of the old menu should not close the new one.

Views: … · One view per browser per day

Was this useful?Total score: …

One reaction per browser. Click again to remove it, or choose the other reaction.

Sources and documentation

Minecraftplugin developmentPaper

Read on

Paper Menu: Stop Item Transfer and Safely Close Window - LonelyCraft — Lonely Craft