diff --git a/paper-api/src/main/java/org/bukkit/Registry.java b/paper-api/src/main/java/org/bukkit/Registry.java index d1906d150a..883338632b 100644 --- a/paper-api/src/main/java/org/bukkit/Registry.java +++ b/paper-api/src/main/java/org/bukkit/Registry.java @@ -27,6 +27,7 @@ import org.bukkit.entity.memory.MemoryKey; import org.bukkit.generator.structure.Structure; import org.bukkit.generator.structure.StructureType; import org.bukkit.inventory.ItemType; +import org.bukkit.inventory.MenuType; import org.bukkit.inventory.meta.trim.TrimMaterial; import org.bukkit.inventory.meta.trim.TrimPattern; import org.bukkit.loot.LootTables; @@ -173,6 +174,13 @@ public interface Registry extends Iterable { * @see Material */ Registry MATERIAL = new SimpleRegistry<>(Material.class, (mat) -> !mat.isLegacy()); + /** + * Server menus. + * + * @see MenuType + */ + @ApiStatus.Experimental + Registry MENU = Objects.requireNonNull(Bukkit.getRegistry(MenuType.class), "No registry present for MenuType. This is a bug."); /** * Server mob effects. * diff --git a/paper-api/src/main/java/org/bukkit/event/inventory/InventoryType.java b/paper-api/src/main/java/org/bukkit/event/inventory/InventoryType.java index c5734d6668..094963b16e 100644 --- a/paper-api/src/main/java/org/bukkit/event/inventory/InventoryType.java +++ b/paper-api/src/main/java/org/bukkit/event/inventory/InventoryType.java @@ -1,8 +1,10 @@ package org.bukkit.event.inventory; import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.MenuType; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Represents the different kinds of inventories available in Bukkit. @@ -26,148 +28,150 @@ public enum InventoryType { * A chest inventory, with 0, 9, 18, 27, 36, 45, or 54 slots of type * CONTAINER. */ - CHEST(27, "Chest"), + CHEST(27, "Chest", MenuType.GENERIC_9X3), /** * A dispenser inventory, with 9 slots of type CONTAINER. */ - DISPENSER(9, "Dispenser"), + DISPENSER(9, "Dispenser", MenuType.GENERIC_3X3), /** * A dropper inventory, with 9 slots of type CONTAINER. */ - DROPPER(9, "Dropper"), + DROPPER(9, "Dropper", MenuType.GENERIC_3X3), /** * A furnace inventory, with a RESULT slot, a CRAFTING slot, and a FUEL * slot. */ - FURNACE(3, "Furnace"), + FURNACE(3, "Furnace", MenuType.FURNACE), /** * A workbench inventory, with 9 CRAFTING slots and a RESULT slot. */ - WORKBENCH(10, "Crafting"), + WORKBENCH(10, "Crafting", MenuType.CRAFTING), /** * A player's crafting inventory, with 4 CRAFTING slots and a RESULT slot. * Also implies that the 4 ARMOR slots are accessible. */ - CRAFTING(5, "Crafting", false), + CRAFTING(5, "Crafting", null, false), /** * An enchantment table inventory, with two CRAFTING slots and three * enchanting buttons. */ - ENCHANTING(2, "Enchanting"), + ENCHANTING(2, "Enchanting", MenuType.ENCHANTMENT), /** * A brewing stand inventory, with one FUEL slot and four CRAFTING slots. */ - BREWING(5, "Brewing"), + BREWING(5, "Brewing", MenuType.BREWING_STAND), /** * A player's inventory, with 9 QUICKBAR slots, 27 CONTAINER slots, 4 ARMOR * slots and 1 offhand slot. The ARMOR and offhand slots may not be visible * to the player, though. */ - PLAYER(41, "Player"), + PLAYER(41, "Player", MenuType.GENERIC_9X4), /** * The creative mode inventory, with only 9 QUICKBAR slots and nothing * else. (The actual creative interface with the items is client-side and * cannot be altered by the server.) */ - CREATIVE(9, "Creative", false), + CREATIVE(9, "Creative", null, false), /** * The merchant inventory, with 2 CRAFTING slots, and 1 RESULT slot. */ - MERCHANT(3, "Villager", false), + MERCHANT(3, "Villager", MenuType.MERCHANT, false), /** * The ender chest inventory, with 27 slots. */ - ENDER_CHEST(27, "Ender Chest"), + ENDER_CHEST(27, "Ender Chest", MenuType.GENERIC_9X3), /** * An anvil inventory, with 2 CRAFTING slots and 1 RESULT slot */ - ANVIL(3, "Repairing"), + ANVIL(3, "Repairing", MenuType.ANVIL), /** * A smithing inventory, with 3 CRAFTING slots and 1 RESULT slot. */ - SMITHING(4, "Upgrade Gear"), + SMITHING(4, "Upgrade Gear", MenuType.SMITHING), /** * A beacon inventory, with 1 CRAFTING slot */ - BEACON(1, "container.beacon"), + BEACON(1, "container.beacon", MenuType.BEACON), /** * A hopper inventory, with 5 slots of type CONTAINER. */ - HOPPER(5, "Item Hopper"), + HOPPER(5, "Item Hopper", MenuType.HOPPER), /** * A shulker box inventory, with 27 slots of type CONTAINER. */ - SHULKER_BOX(27, "Shulker Box"), + SHULKER_BOX(27, "Shulker Box", MenuType.SHULKER_BOX), /** * A barrel box inventory, with 27 slots of type CONTAINER. */ - BARREL(27, "Barrel"), + BARREL(27, "Barrel", MenuType.GENERIC_9X3), /** * A blast furnace inventory, with a RESULT slot, a CRAFTING slot, and a * FUEL slot. */ - BLAST_FURNACE(3, "Blast Furnace"), + BLAST_FURNACE(3, "Blast Furnace", MenuType.BLAST_FURNACE), /** * A lectern inventory, with 1 BOOK slot. */ - LECTERN(1, "Lectern"), + LECTERN(1, "Lectern", MenuType.LECTERN), /** * A smoker inventory, with a RESULT slot, a CRAFTING slot, and a FUEL slot. */ - SMOKER(3, "Smoker"), + SMOKER(3, "Smoker", MenuType.SMOKER), /** * Loom inventory, with 3 CRAFTING slots, and 1 RESULT slot. */ - LOOM(4, "Loom"), + LOOM(4, "Loom", MenuType.LOOM), /** * Cartography inventory with 2 CRAFTING slots, and 1 RESULT slot. */ - CARTOGRAPHY(3, "Cartography Table"), + CARTOGRAPHY(3, "Cartography Table", MenuType.CARTOGRAPHY_TABLE), /** * Grindstone inventory with 2 CRAFTING slots, and 1 RESULT slot. */ - GRINDSTONE(3, "Repair & Disenchant"), + GRINDSTONE(3, "Repair & Disenchant", MenuType.GRINDSTONE), /** * Stonecutter inventory with 1 CRAFTING slot, and 1 RESULT slot. */ - STONECUTTER(2, "Stonecutter"), + STONECUTTER(2, "Stonecutter", MenuType.STONECUTTER), /** * Pseudo composter inventory with 0 or 1 slots of undefined type. */ - COMPOSTER(1, "Composter", false), + COMPOSTER(1, "Composter", null, false), /** * Pseudo chiseled bookshelf inventory, with 6 slots of undefined type. */ - CHISELED_BOOKSHELF(6, "Chiseled Bookshelf", false), + CHISELED_BOOKSHELF(6, "Chiseled Bookshelf", null, false), /** * Pseudo jukebox inventory with 1 slot of undefined type. */ - JUKEBOX(1, "Jukebox", false), + JUKEBOX(1, "Jukebox", null, false), /** * A crafter inventory, with 9 CRAFTING slots. */ @ApiStatus.Experimental - CRAFTER(9, "Crafter"), + CRAFTER(9, "Crafter", MenuType.CRAFTER_3X3), /** * The new smithing inventory, with 3 CRAFTING slots and 1 RESULT slot. * * @deprecated use {@link #SMITHING} */ @Deprecated - SMITHING_NEW(4, "Upgrade Gear"), + SMITHING_NEW(4, "Upgrade Gear", MenuType.SMITHING), ; private final int size; private final String title; + private final MenuType menuType; private final boolean isCreatable; - private InventoryType(int defaultSize, /*@NotNull*/ String defaultTitle) { - this(defaultSize, defaultTitle, true); + private InventoryType(int defaultSize, /*@NotNull*/ String defaultTitle, @Nullable MenuType type) { + this(defaultSize, defaultTitle, type, true); } - private InventoryType(int defaultSize, /*@NotNull*/ String defaultTitle, boolean isCreatable) { + private InventoryType(int defaultSize, /*@NotNull*/ String defaultTitle, @Nullable MenuType type, boolean isCreatable) { size = defaultSize; title = defaultTitle; + this.menuType = type; this.isCreatable = isCreatable; } @@ -180,6 +184,28 @@ public enum InventoryType { return title; } + /** + * Gets the corresponding {@link MenuType} of this InventoryType. + *

+ * Not all InventoryType correspond to a {@link MenuType}. These + * InventoryTypes are also not creatable. If this method returns null, + * {@link #isCreatable()} will return false, with the exception of + * {@link #MERCHANT}. + *

+ * As well as not necessarily corresponding to a {@link MenuType} some + * InventoryType correspond to the same {@link MenuType}, including: + *

    + *
  • Dropper, Dispenser + *
  • ShulkerBox, Barrel, Chest + *
+ * + * @return the corresponding {@link MenuType} + */ + @Nullable + public MenuType getMenuType() { + return menuType; + } + /** * Denotes that this InventoryType can be created via the normal * {@link org.bukkit.Bukkit#createInventory} methods. diff --git a/paper-api/src/main/java/org/bukkit/inventory/MenuType.java b/paper-api/src/main/java/org/bukkit/inventory/MenuType.java new file mode 100644 index 0000000000..ee39bf9019 --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/inventory/MenuType.java @@ -0,0 +1,191 @@ +package org.bukkit.inventory; + +import com.google.common.base.Preconditions; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.view.AnvilView; +import org.bukkit.inventory.view.BeaconView; +import org.bukkit.inventory.view.BrewingStandView; +import org.bukkit.inventory.view.CrafterView; +import org.bukkit.inventory.view.EnchantmentView; +import org.bukkit.inventory.view.FurnaceView; +import org.bukkit.inventory.view.LecternView; +import org.bukkit.inventory.view.LoomView; +import org.bukkit.inventory.view.MerchantView; +import org.bukkit.inventory.view.StonecutterView; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * Represents different kinds of views, also known as menus, which can be + * created and viewed by the player. + */ +@ApiStatus.Experimental +public interface MenuType extends Keyed { + + /** + * A MenuType which represents a chest with 1 row. + */ + MenuType.Typed GENERIC_9X1 = get("generic_9x1"); + /** + * A MenuType which represents a chest with 2 rows. + */ + MenuType.Typed GENERIC_9X2 = get("generic_9x2"); + /** + * A MenuType which represents a chest with 3 rows. + */ + MenuType.Typed GENERIC_9X3 = get("generic_9x3"); + /** + * A MenuType which represents a chest with 4 rows. + */ + MenuType.Typed GENERIC_9X4 = get("generic_9x4"); + /** + * A MenuType which represents a chest with 5 rows. + */ + MenuType.Typed GENERIC_9X5 = get("generic_9x5"); + /** + * A MenuType which represents a chest with 6 rows. + */ + MenuType.Typed GENERIC_9X6 = get("generic_9x6"); + /** + * A MenuType which represents a dispenser/dropper like menu with 3 columns + * and 3 rows. + */ + MenuType.Typed GENERIC_3X3 = get("generic_3x3"); + /** + * A MenuType which represents a crafter + */ + MenuType.Typed CRAFTER_3X3 = get("crafter_3x3"); + /** + * A MenuType which represents an anvil. + */ + MenuType.Typed ANVIL = get("anvil"); + /** + * A MenuType which represents a beacon. + */ + MenuType.Typed BEACON = get("beacon"); + /** + * A MenuType which represents a blast furnace. + */ + MenuType.Typed BLAST_FURNACE = get("blast_furnace"); + /** + * A MenuType which represents a brewing stand. + */ + MenuType.Typed BREWING_STAND = get("brewing_stand"); + /** + * A MenuType which represents a crafting table. + */ + MenuType.Typed CRAFTING = get("crafting"); + /** + * A MenuType which represents an enchantment table. + */ + MenuType.Typed ENCHANTMENT = get("enchantment"); + /** + * A MenuType which represents a furnace. + */ + MenuType.Typed FURNACE = get("furnace"); + /** + * A MenuType which represents a grindstone. + */ + MenuType.Typed GRINDSTONE = get("grindstone"); + /** + * A MenuType which represents a hopper. + */ + MenuType.Typed HOPPER = get("hopper"); + /** + * A MenuType which represents a lectern, a book like view. + */ + MenuType.Typed LECTERN = get("lectern"); + /** + * A MenuType which represents a loom. + */ + MenuType.Typed LOOM = get("loom"); + /** + * A MenuType which represents a merchant. + */ + MenuType.Typed MERCHANT = get("merchant"); + /** + * A MenuType which represents a shulker box. + */ + MenuType.Typed SHULKER_BOX = get("shulker_box"); + /** + * A MenuType which represents a stonecutter. + */ + MenuType.Typed SMITHING = get("smithing"); + /** + * A MenuType which represents a smoker. + */ + MenuType.Typed SMOKER = get("smoker"); + /** + * A MenuType which represents a cartography table. + */ + MenuType.Typed CARTOGRAPHY_TABLE = get("cartography_table"); + /** + * A MenuType which represents a stonecutter. + */ + MenuType.Typed STONECUTTER = get("stonecutter"); + + /** + * Typed represents a subtype of {@link MenuType}s that have a known + * {@link InventoryView} type at compile time. + * + * @param the generic type of {@link InventoryView} that represents the + * view type. + */ + interface Typed extends MenuType { + + /** + * Creates a view of the specified menu type. + *

+ * The player provided to create this view must be the player the view + * is opened for. See {@link HumanEntity#openInventory(InventoryView)} + * for more information. + * + * @param player the player the view belongs to + * @param title the title of the view + * @return the created {@link InventoryView} + */ + @NotNull + V create(@NotNull HumanEntity player, @NotNull String title); + } + + /** + * Yields this MenuType as a typed version of itself with a plain + * {@link InventoryView} representing it. + * + * @return the typed MenuType. + */ + @NotNull + MenuType.Typed typed(); + + /** + * Yields this MenuType as a typed version of itself with a specific + * {@link InventoryView} representing it. + * + * @param viewClass the class type of the {@link InventoryView} to type this + * {@link InventoryView} with. + * @param the generic type of the InventoryView to get this MenuType + * with + * @return the typed MenuType + * @throws IllegalArgumentException if the provided viewClass cannot be + * typed to this MenuType + */ + @NotNull + MenuType.Typed typed(@NotNull final Class viewClass) throws IllegalArgumentException; + + /** + * Gets the {@link InventoryView} class of this MenuType. + * + * @return the {@link InventoryView} class of this MenuType + */ + @NotNull + Class getInventoryViewClass(); + + private static T get(@NotNull final String key) { + final MenuType type = Registry.MENU.get(NamespacedKey.minecraft(key)); + Preconditions.checkArgument(type != null, "The given string key must be an existing menu type"); + return (T) type; + } +}