Class MenuSwitch

java.lang.Object
com.trevlar.slotkit.api.menu.MenuSwitch

public final class MenuSwitch extends Object
Switching a player between menus while one menu, the one they opened in the world, waits open behind the others. For tabs on a container screen: a player at a chest flips to their inventory, a shulker box, their ender chest, and back, and the chest stays open in the world the whole time (its lid up, no close sound), while every other menu really closes when they leave it (the inventory's crafting grid hands its items back).
// Server, from a tab click:
MenuSwitch.open(player, boxProvider);     // the chest waits behind, the box opens
MenuSwitch.openInventory(player);         // the box closes, the inventory opens
MenuSwitch.back(player);                  // the inventory closes, the chest comes back

// Common init: hear when the waiting menu closes on its own.
MenuSwitch.onSetAsideClosed((player, menu, why) -> ...);

The menu set aside

The first switch away from a menu the player opened some other way (a chest they clicked, any player.openMenu that isn't this class's) sets it aside instead of closing it: no removed(), no close packet, so the block's opener count holds. Menus opened through open(ServerPlayer, MenuProvider) are the tabs: switching away from one closes it normally. The inventory is never set aside, so a switch that starts at the inventory screen sets nothing aside, and back(ServerPlayer) has nothing to return to. Only a menu opened through openMenu, or a mount's inventory (a horse's, a llama's, a nautilus's), can be set aside: back sends the packet that opened it again. Any other closes normally. A trading screen comes back with its trades (the selected one may reset); its trader stays locked to the player while it waits.

While it waits, the set-aside menu's stillValid is checked every tick, and it closes properly (removed(), so the lid shuts) when that fails, the player dies or changes dimension, the menu in front closes (Escape on any screen), a menu opens some other way, or the player leaves. onSetAsideClosed(MenuSwitch.SetAsideClosed) hears each of these. It gets no sync while it waits; back resyncs it whole.

The cursor

The stack on the cursor moves across every switch, to the next menu, instead of going back to the inventory.

Clicks in flight

A click the client sent before it heard of a switch is addressed to the menu it left, and vanilla drops it. Until the client's first click on the new menu, such a click on one of the player's inventory slots (any slot whose container the new menu also shows) lands on that same slot in the new menu: a pickup, a put-down, a number key, a middle click or a drop key, or a click outside the screen. A click on the old menu's own slots, a shift-click, a double-click collect and a drag are still dropped, since what they would do depends on the menu they were aimed at.

The client

open and back send only vanilla packets, so they work for a player without SlotKit. openInventory(ServerPlayer) needs SlotKit on the client (vanilla has no packet that opens the inventory) and returns false without it. With SlotKit on the client, the screen the player is on stays up until the next menu's contents arrive, so a switch never shows an empty screen for a frame.

Server thread only. Since 2.1.0.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static enum 
    Why the set-aside menu closed.
    static interface 
    Hears the set-aside menu close on its own.
  • Method Summary

    Modifier and Type
    Method
    Description
    static boolean
    back(net.minecraft.server.level.ServerPlayer player)
    Closes the menu in front normally and reopens the set-aside one as itself: its container id, type and title, then all its contents.
    static net.minecraft.network.protocol.game.ServerboundContainerClickPacket
    clickArrived(net.minecraft.server.level.ServerPlayer player, net.minecraft.network.protocol.game.ServerboundContainerClickPacket click)
    A click from the player's client, on the server thread before vanilla handles it: the same click on the menu in front when it was aimed at the menu just switched away from (see "Clicks in flight" in the class doc), otherwise click itself.
    static void
    frontClosed(net.minecraft.server.level.ServerPlayer player)
    The menu in front closed: unless that was a switch, the set-aside one closes too.
    static int
    heldId(net.minecraft.server.level.ServerPlayer player)
    The container id the player's next menu must not take while one is set aside, or -1.
    static void
    left(net.minecraft.server.level.ServerPlayer player)
    The player is leaving the server.
    static void
    menuOpened(net.minecraft.server.level.ServerPlayer player, net.minecraft.network.chat.Component title)
    openMenu opened the player's new menu under title.
    static void
    menuOpening(net.minecraft.world.inventory.AbstractContainerMenu menu)
    openMenu made menu, just before its first sync: an open(ServerPlayer, MenuProvider) gets its cursor stack.
    static void
    mountOpened(net.minecraft.server.level.ServerPlayer player, int inventoryColumns, int mountId)
    A mount's inventory opened for the player (openHorseInventory, openNautilusInventory), which skips openMenu.
    static void
    offersSent(net.minecraft.server.level.ServerPlayer player, int containerId, int traderLevel)
    The player's trading menu containerId was sent its offers at traderLevel.
    static void
    Adds a listener for the set-aside menu closing on its own.
    open(net.minecraft.server.level.ServerPlayer player, net.minecraft.world.MenuProvider next)
    Opens next for player.
    static boolean
    openInventory(net.minecraft.server.level.ServerPlayer player)
    Opens the player's own inventory screen, as open(ServerPlayer, MenuProvider) does a menu: the menu in front is set aside or closes, and the cursor stack moves over.
    static void
    removed(net.minecraft.server.level.ServerPlayer player)
    The player's entity is leaving its level: a dimension change, a respawn (which replaces the entity), the End's exit, or leaving the server.
    static Optional<net.minecraft.world.inventory.AbstractContainerMenu>
    setAside(net.minecraft.server.level.ServerPlayer player)
    The menu player has set aside, if any.
    static void
    tick(net.minecraft.server.level.ServerPlayer player)
    Each player tick: the set-aside menu still has to be valid.

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • open

      public static OptionalInt open(net.minecraft.server.level.ServerPlayer player, net.minecraft.world.MenuProvider next)
      Opens next for player. The menu in front is set aside if it can be (see the class doc) and nothing is set aside yet, and otherwise closes normally. The cursor stack moves to next.
      Returns:
      the new menu's container id, or empty when next made no menu. Then the player goes back to the set-aside menu, or with none, the screen closes and the cursor stack returns to the inventory.
    • openInventory

      public static boolean openInventory(net.minecraft.server.level.ServerPlayer player)
      Opens the player's own inventory screen, as open(ServerPlayer, MenuProvider) does a menu: the menu in front is set aside or closes, and the cursor stack moves over.
      Returns:
      false, and nothing changes, when the player's client has no SlotKit
    • back

      public static boolean back(net.minecraft.server.level.ServerPlayer player)
      Closes the menu in front normally and reopens the set-aside one as itself: its container id, type and title, then all its contents. The cursor stack moves over.
      Returns:
      false when nothing is set aside
    • setAside

      public static Optional<net.minecraft.world.inventory.AbstractContainerMenu> setAside(net.minecraft.server.level.ServerPlayer player)
      The menu player has set aside, if any.
    • onSetAsideClosed

      public static void onSetAsideClosed(MenuSwitch.SetAsideClosed listener)
      Adds a listener for the set-aside menu closing on its own. Common init.
    • mountOpened

      @Internal public static void mountOpened(net.minecraft.server.level.ServerPlayer player, int inventoryColumns, int mountId)
      A mount's inventory opened for the player (openHorseInventory, openNautilusInventory), which skips openMenu.
    • offersSent

      @Internal public static void offersSent(net.minecraft.server.level.ServerPlayer player, int containerId, int traderLevel)
      The player's trading menu containerId was sent its offers at traderLevel.
    • frontClosed

      @Internal public static void frontClosed(net.minecraft.server.level.ServerPlayer player)
      The menu in front closed: unless that was a switch, the set-aside one closes too.
    • tick

      @Internal public static void tick(net.minecraft.server.level.ServerPlayer player)
      Each player tick: the set-aside menu still has to be valid.
    • left

      @Internal public static void left(net.minecraft.server.level.ServerPlayer player)
      The player is leaving the server.
    • removed

      @Internal public static void removed(net.minecraft.server.level.ServerPlayer player)
      The player's entity is leaving its level: a dimension change, a respawn (which replaces the entity), the End's exit, or leaving the server. A respawned or departed entity never ticks again, so its set-aside menu closes now or never (a crafting grid's items would be lost), and this class lets go of it. Each map entry's menu holds its player, so a weak key alone never frees it.
    • clickArrived

      @Internal public static net.minecraft.network.protocol.game.ServerboundContainerClickPacket clickArrived(net.minecraft.server.level.ServerPlayer player, net.minecraft.network.protocol.game.ServerboundContainerClickPacket click)
      A click from the player's client, on the server thread before vanilla handles it: the same click on the menu in front when it was aimed at the menu just switched away from (see "Clicks in flight" in the class doc), otherwise click itself.
    • heldId

      @Internal public static int heldId(net.minecraft.server.level.ServerPlayer player)
      The container id the player's next menu must not take while one is set aside, or -1.