> panes = new ArrayList<>();
+
+ /**
+ * The current page
+ */
+ private int page;
+
+ /**
+ * Creates a new paginated pane
+ *
+ * @param length the length of the pane
+ * @param height the height of the pane
+ * @param priority the priority of the pane
+ * @since 0.12.0
+ */
+ public PaginatedPane(int length, int height, @NotNull Priority priority) {
+ super(length, height, priority);
+ }
+
+ /**
+ * Creates a new paginated pane
+ *
+ * @param length the length of the pane
+ * @param height the height of the pane
+ * @since 0.12.0
+ */
+ public PaginatedPane(int length, int height) {
+ this(length, height, Priority.NORMAL);
+ }
+
+ /**
+ * Returns the current page
+ *
+ * @return the current page
+ */
+ public int getPage() {
+ return page;
+ }
+
+ /**
+ * Returns the amount of pages
+ *
+ * @return the amount of pages
+ */
+ public int getPages() {
+ return panes.size();
+ }
+
+ /**
+ * Adds the specified pane to a new page. The new page will be at the index one after the highest indexed page
+ * currently in this paginated pane. If this paginated pane has no pages, the index of the newly created page will
+ * be zero.
+ *
+ * @param slot the slot of the pane
+ * @param pane the pane to add to a new page
+ * @since 0.10.8
+ */
+ public void addPage(@NotNull Slot slot, @NotNull Pane pane) {
+ List list = new ArrayList<>(1);
+
+ list.add(new PositionedPane(slot, pane));
+
+ this.panes.add(list);
+ }
+
+ /**
+ * Adds a pane to a selected page. If the page does not exist and is exactly one larger than the current highest
+ * page index, this method will create a new page with the specified pane. If the page does not exist and is more
+ * than one larger than the current highest page index, this method will throw an {@link IllegalArgumentException}.
+ * If the page is negative, an {@link IllegalArgumentException} will also be thrown. If there are currently no
+ * pages, only index 0 is valid and will create a new page with the specified pane added to it.
+ *
+ * For example, if the pages 0, 1, ..., n currently exist, then:
+ *
+ * Indexes < 0 will throw an exception
+ * Indexes 0, 1, ..., n will add the pane to the respective page
+ * Index n + 1 will create a new page with the specified pane
+ * Indexes > n + 1 will throw an exception
+ *
+ *
+ * @param page the page to assign the pane to
+ * @param slot the slot of the pane
+ * @param pane the new pane
+ * @throws IllegalArgumentException if the page is less than 0 or more than one larger than the current highest page
+ * index
+ */
+ public void addPane(int page, @NotNull Slot slot, @NotNull Pane pane) {
+ if (page < 0) {
+ throw new IllegalArgumentException("Non-positive page indexes are not allowed");
+ }
+
+ if (page > this.panes.size()) {
+ throw new IllegalArgumentException("Page index outside range of existing pages");
+ }
+
+ if (page == this.panes.size()) {
+ addPage(slot, pane);
+ } else {
+ this.panes.get(page).add(new PositionedPane(slot, pane));
+
+ this.panes.get(page).sort(Comparator.comparing(positionedPane -> positionedPane.getPane().getPriority()));
+ }
+ }
+
+ /**
+ * Sets the current displayed page. If the specified page does not exist an {@link ArrayIndexOutOfBoundsException}
+ * is thrown.
+ *
+ * @param page the page
+ * @throws ArrayIndexOutOfBoundsException if the page does not exist
+ */
+ public void setPage(int page) {
+ if (page < 0 || page >= this.panes.size()) {
+ throw new ArrayIndexOutOfBoundsException("Page outside of range");
+ }
+
+ this.page = page;
+ }
+
+ /**
+ * Populates the PaginatedPane based on the provided list by adding new pages until all items can fit.
+ * This can be helpful when dealing with lists of unknown size.
+ *
+ * @param items The list to populate the pane with
+ * @param plugin the plugin that will be the owner of the items created
+ * @see #populateWithItemStacks(List)
+ * @since 0.10.8
+ */
+ public void populateWithItemStacks(@NotNull List<@NotNull ItemStack> items, @NotNull Plugin plugin) {
+ //Don't do anything if the list is empty
+ if (items.isEmpty()) {
+ return;
+ }
+
+ int itemsPerPage = this.height * this.length;
+ int pagesNeeded = (int) Math.max(Math.ceil(items.size() / (double) itemsPerPage), 1);
+
+ for (int i = 0; i < pagesNeeded; i++) {
+ OutlinePane page = new OutlinePane(this.length, this.height);
+
+ for (int j = 0; j < itemsPerPage; j++) {
+ //Check if the loop reached the end of the list
+ int index = i * itemsPerPage + j;
+
+ if (index >= items.size()) {
+ break;
+ }
+
+ page.addItem(new GuiItem(items.get(index), plugin));
+ }
+
+ this.addPane(i, Slot.fromIndex(0), page);
+ }
+ }
+
+ /**
+ * Populates the PaginatedPane based on the provided list by adding new pages until all items can fit.
+ * This can be helpful when dealing with lists of unknown size.
+ *
+ * @param items The list to populate the pane with
+ */
+ public void populateWithItemStacks(@NotNull List items) {
+ populateWithItemStacks(items, JavaPlugin.getProvidingPlugin(PaginatedPane.class));
+ }
+
+ /**
+ * Populates the PaginatedPane based on the provided list by adding new pages until all items can fit.
+ * This can be helpful when dealing with lists of unknown size.
+ *
+ * @param items The list to populate the pane with
+ */
+ @Contract("null -> fail")
+ public void populateWithGuiItems(@NotNull List items) {
+ //Don't do anything if the list is empty
+ if (items.isEmpty()) {
+ return;
+ }
+
+ int itemsPerPage = this.height * this.length;
+ int pagesNeeded = (int) Math.max(Math.ceil(items.size() / (double) itemsPerPage), 1);
+
+ for (int i = 0; i < pagesNeeded; i++) {
+ OutlinePane page = new OutlinePane(this.length, this.height);
+
+ for (int j = 0; j < itemsPerPage; j++) {
+ int index = i * itemsPerPage + j;
+
+ //Check if the loop reached the end of the list
+ if (index >= items.size()) {
+ break;
+ }
+
+ page.addItem(items.get(index));
+ }
+
+ this.addPane(i, Slot.fromIndex(0), page);
+ }
+ }
+
+ /**
+ * This method creates a list of ItemStacks all with the given {@code material} and the display names.
+ * After that it calls {@link #populateWithItemStacks(List)}
+ * This method also translates the color char {@code &} for all names.
+ *
+ * @param displayNames The display names for all the items
+ * @param material The material to use for the {@link org.bukkit.inventory.ItemStack}s
+ * @param plugin the plugin that will be the owner of the created items
+ * @see #populateWithNames(List, Material)
+ * @since 0.10.8
+ */
+ public void populateWithNames(@NotNull List displayNames, @Nullable Material material,
+ @NotNull Plugin plugin) {
+ if(material == null || material == Material.AIR) return;
+
+ populateWithItemStacks(displayNames.stream().map(name -> {
+ ItemStack itemStack = new ItemStack(material);
+ ItemMeta itemMeta = itemStack.getItemMeta();
+ itemMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', name));
+ itemStack.setItemMeta(itemMeta);
+ return itemStack;
+ }).collect(Collectors.toList()), plugin);
+ }
+
+ /**
+ * This method creates a list of ItemStacks all with the given {@code material} and the display names.
+ * After that it calls {@link #populateWithItemStacks(List)}
+ * This method also translates the color char {@code &} for all names.
+ *
+ * @param displayNames The display names for all the items
+ * @param material The material to use for the {@link org.bukkit.inventory.ItemStack}s
+ */
+ public void populateWithNames(@NotNull List displayNames, @Nullable Material material) {
+ populateWithNames(displayNames, material, JavaPlugin.getProvidingPlugin(PaginatedPane.class));
+ }
+
+ @NotNull
+ @Override
+ public GuiItemContainer display() {
+ GuiItemContainer container = new GuiItemContainer(getLength(), getHeight());
+
+ if (this.page < 0 || this.page >= this.panes.size()) {
+ return container;
+ }
+
+ List panes = this.panes.get(page);
+
+ if (panes == null) {
+ return container;
+ }
+
+ for (PositionedPane positionedPane : panes) {
+ Pane pane = positionedPane.getPane();
+
+ if (!pane.isVisible()) {
+ continue;
+ }
+
+ Slot slot = positionedPane.getSlot();
+
+ container.apply(pane.display(), slot.getX(getLength()), slot.getY(getLength()));
+ }
+
+ return container;
+ }
+
+ @Override
+ public boolean click(@NotNull Gui gui, @NotNull GuiComponent guiComponent, @NotNull InventoryClickEvent event,
+ @NotNull Slot slot) {
+ int x = slot.getX(getLength());
+ int y = slot.getY(getLength());
+
+ //this isn't our item
+ if (x < 0 || x >= getLength() || y < 0 || y >= getHeight()) {
+ return false;
+ }
+
+ callOnClick(event);
+
+ boolean success = false;
+
+ if (this.page < 0 || this.page >= this.panes.size()) {
+ return false;
+ }
+
+ for (PositionedPane positionedPane : this.panes.get(this.page)) {
+ Pane pane = positionedPane.getPane();
+
+ if (!pane.isVisible()) {
+ continue;
+ }
+
+ Slot paneSlot = positionedPane.getSlot();
+ Slot innerSlot = Slot.fromXY(x - paneSlot.getX(getLength()), y - paneSlot.getY(getLength()));
+
+ success = success || pane.click(gui, guiComponent, event, innerSlot);
+ }
+
+ return success;
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public PaginatedPane copy() {
+ PaginatedPane paginatedPane = new PaginatedPane(this.length, this.height, getPriority());
+
+ for (int page = 0; page < this.panes.size(); page++) {
+ List extends PositionedPane> panes = this.panes.get(page);
+
+ for (PositionedPane positionedPane : panes) {
+ paginatedPane.addPane(page, positionedPane.getSlot(), positionedPane.getPane().copy());
+ }
+ }
+
+ paginatedPane.setVisible(isVisible());
+ paginatedPane.onClick = this.onClick;
+
+ paginatedPane.uuid = this.uuid;
+
+ paginatedPane.page = this.page;
+
+ return paginatedPane;
+ }
+
+ /**
+ * Deletes a page and all its associated panes from this paginated pane. It also decrements the indexes of all pages
+ * beyond the specified page by one. For example, given a sequence of pages 0, 1, 2, 3, 4, upon removing page 2, the
+ * new sequence of pages will be 0, 1, 2, 3. If the specified page does not exist, then this method will silently do
+ * nothing.
+ *
+ * @param page the page to delete
+ * @since 0.10.5
+ */
+ public void deletePage(int page) {
+ if (page < 0 || page >= this.panes.size()) {
+ return;
+ }
+
+ this.panes.remove(page);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Collection getPanes() {
+ Collection panes = new HashSet<>();
+
+ for (List extends PositionedPane> positionedPanes : this.panes) {
+ for (PositionedPane positionedPane : positionedPanes) {
+ panes.add(positionedPane.getPane());
+ }
+ }
+
+ return panes;
+ }
+
+ /**
+ * Gets all the panes from inside the specified page of this pane. If the specified page is not existent, this
+ * method will throw an {@link IllegalArgumentException}. If the specified page is existent, but doesn't
+ * have any panes, the returned collection will be empty. The returned collection is unmodifiable. The returned
+ * collection is not synchronized and no guarantees should be made as to the safety of concurrently accessing the
+ * returned collection. If synchronized behaviour should be allowed, the returned collection must be synchronized
+ * externally.
+ *
+ * @param page the panes of this page will be returned
+ * @return a collection of panes belonging to the specified page
+ * @since 0.5.13
+ * @throws IllegalArgumentException if the page does not exist
+ */
+ @NotNull
+ @Contract(pure = true)
+ public Collection getPanes(int page) {
+ if (page < 0 || this.page >= this.panes.size()) {
+ throw new IllegalArgumentException("Invalid page");
+ }
+
+ Collection extends PositionedPane> positionedPanes = this.panes.get(page);
+
+ if (positionedPanes == null) {
+ throw new IllegalArgumentException("Invalid page");
+ }
+
+ Collection panes = new HashSet<>(positionedPanes.size());
+
+ for (PositionedPane positionedPane : positionedPanes) {
+ panes.add(positionedPane.getPane());
+ }
+
+ return Collections.unmodifiableCollection(panes);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Collection getItems() {
+ return getPanes().stream().flatMap(pane -> pane.getItems().stream()).collect(Collectors.toList());
+ }
+
+ @Override
+ public void clear() {
+ panes.clear();
+ }
+
+ /**
+ * Loads a paginated pane from a given element
+ *
+ * @param instance the instance class
+ * @param element the element
+ * @param plugin the plugin that will be used to create the items
+ * @return the paginated pane
+ * @since 0.10.8
+ */
+ @NotNull
+ public static PaginatedPane load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) {
+ if (!element.hasAttribute("length")) {
+ throw new XMLLoadException("Paginated pane XML tag does not have the mandatory length attribute");
+ }
+
+ if (!element.hasAttribute("height")) {
+ throw new XMLLoadException("Paginated pane XML tag does not have the mandatory height attribute");
+ }
+
+ int length;
+ int height;
+
+ try {
+ length = Integer.parseInt(element.getAttribute("length"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Length attribute is not an integer", exception);
+ }
+
+ try {
+ height = Integer.parseInt(element.getAttribute("height"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Height attribute is not an integer", exception);
+ }
+
+ PaginatedPane paginatedPane = new PaginatedPane(length, height);
+
+ Pane.load(paginatedPane, instance, element);
+
+ if (element.hasAttribute("id")) {
+ element.setIdAttribute("id", true);
+ element.setUserData("pane", paginatedPane, null);
+ }
+
+ if (element.hasAttribute("populate")) {
+ return paginatedPane;
+ }
+
+ int pageCount = 0;
+
+ NodeList childNodes = element.getChildNodes();
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node item = childNodes.item(i);
+ if (item.getNodeType() != Node.ELEMENT_NODE)
+ continue;
+
+ if(!item.getNodeName().equals("page"))
+ throw new XMLLoadException("Panes have to be inside page tag");
+
+ NodeList innerNodes = item.getChildNodes();
+
+ for (int j = 0; j < innerNodes.getLength(); j++) {
+ Node innerNode = innerNodes.item(j);
+
+ if (innerNode.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+
+ Pane pane = Gui.loadPane(instance, innerNode, plugin);
+
+ paginatedPane.addPane(pageCount, Slot.deserialize((Element) innerNode), pane);
+ }
+
+ pageCount++;
+ }
+
+ return paginatedPane;
+ }
+}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Pane.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Pane.java
index 79a7afed8..39e5113e5 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Pane.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Pane.java
@@ -1,50 +1,34 @@
package com.github.stefvanschie.inventoryframework.pane;
-import com.github.stefvanschie.inventoryframework.gui.InventoryComponent;
+import com.github.stefvanschie.inventoryframework.gui.GuiComponent;
import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
-import com.github.stefvanschie.inventoryframework.exception.XMLReflectionException;
-import com.github.stefvanschie.inventoryframework.util.SkullUtil;
+import com.github.stefvanschie.inventoryframework.pane.util.GuiItemContainer;
+import com.github.stefvanschie.inventoryframework.pane.util.Mask;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
+import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil;
import com.github.stefvanschie.inventoryframework.util.UUIDTagType;
import com.github.stefvanschie.inventoryframework.util.XMLUtil;
-import com.google.common.primitives.Primitives;
-import org.apache.commons.lang3.reflect.MethodUtils;
-import org.bukkit.ChatColor;
-import org.bukkit.Material;
-import org.bukkit.NamespacedKey;
-import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
-import org.bukkit.inventory.meta.SkullMeta;
-import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
import java.lang.UnsupportedOperationException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.logging.Level;
-import java.util.logging.Logger;
/**
* The base class for all panes.
*/
public abstract class Pane {
- /**
- * The starting position of this pane, which is 0 by default
- */
- protected int x = 0, y = 0;
-
/**
* Length is horizontal, height is vertical
*/
@@ -65,36 +49,26 @@ public abstract class Pane {
* The consumer that will be called once a players clicks in this pane
*/
@Nullable
- protected Consumer onClick;
+ protected Consumer super InventoryClickEvent> onClick;
/**
* A unique identifier for panes to locate them by
*/
protected UUID uuid;
- /**
- * A map containing the mappings for properties for items
- */
- @NotNull
- private static final Map> PROPERTY_MAPPINGS = new HashMap<>();
-
/**
* Constructs a new default pane
*
- * @param x the upper left x coordinate of the pane
- * @param y the upper left y coordinate of the pane
* @param length the length of the pane
* @param height the height of the pane
* @param priority the priority of the pane
+ * @since 0.12.0
*/
- protected Pane(int x, int y, int length, int height, @NotNull Priority priority) {
+ protected Pane(int length, int height, @NotNull Priority priority) {
if (length == 0 || height == 0) {
throw new IllegalArgumentException("Length and height of pane must be greater than zero");
}
- this.x = x;
- this.y = y;
-
this.length = length;
this.height = height;
@@ -124,18 +98,6 @@ protected Pane(int length, int height) {
this.uuid = UUID.randomUUID();
}
- /**
- * Constructs a new default pane
- *
- * @param x the upper left x coordinate of the pane
- * @param y the upper left y coordinate of the pane
- * @param length the length of the pane
- * @param height the height of the pane
- */
- protected Pane(int x, int y, int length, int height) {
- this(x, y, length, height, Priority.NORMAL);
- }
-
/**
* Makes a copy of this pane and returns it. This makes a deep copy of the pane. This entails that the underlying
* panes and/or items will be copied as well. The returned pane will never be reference equal to the current pane.
@@ -167,24 +129,6 @@ public void setHeight(int height) {
this.height = height;
}
- /**
- * Set the x coordinate of this pane
- *
- * @param x the new x coordinate
- */
- public void setX(int x) {
- this.x = x;
- }
-
- /**
- * Set the y coordinate of this pane
- *
- * @param y the new y coordinate
- */
- public void setY(int y) {
- this.y = y;
- }
-
/**
* Returns the length of this pane
*
@@ -218,36 +162,13 @@ public UUID getUUID() {
}
/**
- * Gets the x coordinate of this pane
- *
- * @return the x coordinate
- */
- @Contract(pure = true)
- public int getX() {
- return x;
- }
-
- /**
- * Gets the y coordinate of this pane
- *
- * @return the y coordinate
- */
- @Contract(pure = true)
- public int getY() {
- return y;
- }
-
- /**
- * Has to set all the items in the right spot inside the inventory
+ * Returns a {@link GuiItemContainer} with all the items that should be displayed of this pane.
*
- * @param inventoryComponent the inventory component in which the items should be displayed
- * @param paneOffsetX the pane's offset on the x axis
- * @param paneOffsetY the pane's offset on the y axis
- * @param maxLength the maximum length of the pane
- * @param maxHeight the maximum height of the pane
+ * @return a container containing all the items to be displayed
+ * @since 0.12.0
*/
- public abstract void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY,
- int maxLength, int maxHeight);
+ @NotNull
+ public abstract GuiItemContainer display();
/**
* Returns the pane's visibility state
@@ -272,18 +193,13 @@ public void setVisible(boolean visible) {
* Called whenever there is being clicked on this pane
*
* @param gui the gui in which was clicked
- * @param inventoryComponent the inventory component in which this pane resides
+ * @param guiComponent the gui component in which this pane resides
* @param event the event that occurred while clicking on this item
* @param slot the slot that was clicked in
- * @param paneOffsetX the pane's offset on the x axis
- * @param paneOffsetY the pane's offset on the y axis
- * @param maxLength the maximum length of the pane
- * @param maxHeight the maximum height of the pane
* @return whether the item was found or not
*/
- public abstract boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent,
- @NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY,
- int maxLength, int maxHeight);
+ public abstract boolean click(@NotNull Gui gui, @NotNull GuiComponent guiComponent,
+ @NotNull InventoryClickEvent event, @NotNull Slot slot);
/**
* Sets the priority of this pane
@@ -294,216 +210,14 @@ public void setPriority(@NotNull Priority priority) {
this.priority = priority;
}
- /**
- * Loads an item from an instance and an element
- *
- * @param instance the instance
- * @param element the element
- * @return the gui item
- */
- @NotNull
- @Contract(pure = true)
- public static GuiItem loadItem(@NotNull Object instance, @NotNull Element element) {
- String id = element.getAttribute("id");
- Material material = Objects.requireNonNull(Material.matchMaterial(id.toUpperCase(Locale.getDefault())));
- boolean hasAmount = element.hasAttribute("amount");
- boolean hasDamage = element.hasAttribute("damage");
- int amount = hasAmount ? Integer.parseInt(element.getAttribute("amount")) : 1;
- short damage = hasDamage ? Short.parseShort(element.getAttribute("damage")) : 0;
-
- //noinspection deprecation
- ItemStack itemStack = new ItemStack(material, amount, damage);
-
- List properties = new ArrayList<>();
-
- if (element.hasChildNodes()) {
- NodeList childNodes = element.getChildNodes();
-
- for (int i = 0; i < childNodes.getLength(); i++) {
- Node item = childNodes.item(i);
-
- if (item.getNodeType() != Node.ELEMENT_NODE)
- continue;
-
- Element elementItem = (Element) item;
-
- String nodeName = item.getNodeName();
-
- if (nodeName.equals("properties") || nodeName.equals("lore") || nodeName.equals("enchantments")) {
- Element innerElement = (Element) item;
- NodeList innerChildNodes = innerElement.getChildNodes();
-
- for (int j = 0; j < innerChildNodes.getLength(); j++) {
- Node innerNode = innerChildNodes.item(j);
-
- if (innerNode.getNodeType() != Node.ELEMENT_NODE)
- continue;
-
- Element innerElementChild = (Element) innerNode;
- ItemMeta itemMeta = Objects.requireNonNull(itemStack.getItemMeta());
-
- switch (nodeName) {
- case "properties":
- if (!innerNode.getNodeName().equals("property"))
- continue;
-
- String propertyType = innerElementChild.hasAttribute("type")
- ? innerElementChild.getAttribute("type")
- : "string";
-
- properties.add(PROPERTY_MAPPINGS.get(propertyType).apply(innerElementChild
- .getTextContent()));
- break;
- case "lore":
- if (!innerNode.getNodeName().equals("line"))
- continue;
-
- boolean hasLore = itemMeta.hasLore();
- List lore = hasLore ? Objects.requireNonNull(itemMeta.getLore()) : new ArrayList<>();
-
- lore.add(ChatColor.translateAlternateColorCodes('&', innerNode
- .getTextContent()));
- itemMeta.setLore(lore);
- itemStack.setItemMeta(itemMeta);
- break;
- case "enchantments":
- if (!innerNode.getNodeName().equals("enchantment"))
- continue;
-
- Enchantment enchantment = Enchantment.getByKey(NamespacedKey.minecraft(
- innerElementChild.getAttribute("id").toUpperCase(Locale.getDefault())
- ));
-
- if (enchantment == null) {
- throw new XMLLoadException("Enchantment cannot be found");
- }
-
- int level = Integer.parseInt(innerElementChild.getAttribute("level"));
-
- itemMeta.addEnchant(enchantment, level, true);
- itemStack.setItemMeta(itemMeta);
- break;
- }
- }
- } else if (nodeName.equals("displayname")) {
- ItemMeta itemMeta = Objects.requireNonNull(itemStack.getItemMeta());
-
- itemMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', item
- .getTextContent()));
-
- itemStack.setItemMeta(itemMeta);
- } else if (nodeName.equals("skull") && itemStack.getItemMeta() instanceof SkullMeta) {
- SkullMeta skullMeta = (SkullMeta) itemStack.getItemMeta();
-
- if (elementItem.hasAttribute("owner"))
- //noinspection deprecation
- skullMeta.setOwner(elementItem.getAttribute("owner"));
- else if (elementItem.hasAttribute("id")) {
- SkullUtil.setSkull(skullMeta, elementItem.getAttribute("id"));
- }
-
- itemStack.setItemMeta(skullMeta);
- }
- }
- }
-
- Consumer action = null;
-
- if (element.hasAttribute("onClick")) {
- String methodName = element.getAttribute("onClick");
- for (Method method : instance.getClass().getMethods()) {
- if (!method.getName().equals(methodName))
- continue;
-
- int parameterCount = method.getParameterCount();
- Class>[] parameterTypes = method.getParameterTypes();
-
- if (parameterCount == 0)
- action = event -> {
- try {
- //because reflection with lambdas is stupid
- method.setAccessible(true);
- method.invoke(instance);
- } catch (IllegalAccessException | InvocationTargetException exception) {
- throw new XMLReflectionException(exception);
- }
- };
- else if (parameterTypes[0].isAssignableFrom(InventoryClickEvent.class)) {
- if (parameterCount == 1)
- action = event -> {
- try {
- //because reflection with lambdas is stupid
- method.setAccessible(true);
- method.invoke(instance, event);
- } catch (IllegalAccessException | InvocationTargetException exception) {
- throw new XMLReflectionException(exception);
- }
- };
- else if (parameterCount == properties.size() + 1) {
- boolean correct = true;
-
- for (int i = 0; i < properties.size(); i++) {
- Object attribute = properties.get(i);
-
- if (!(parameterTypes[1 + i].isPrimitive() &&
- Primitives.unwrap(attribute.getClass()).isAssignableFrom(parameterTypes[1 + i])) &&
- !attribute.getClass().isAssignableFrom(parameterTypes[1 + i]))
- correct = false;
- }
-
- if (correct) {
- action = event -> {
- try {
- //don't ask me why we need to do this, just roll with it (actually I do know why, but it's stupid)
- properties.add(0, event);
-
- //because reflection with lambdas is stupid
- method.setAccessible(true);
- method.invoke(instance, properties.toArray(new Object[0]));
-
- //since we'll append the event to the list next time again, we need to remove it here again
- properties.remove(0);
- } catch (IllegalAccessException | InvocationTargetException exception) {
- throw new XMLReflectionException(exception);
- }
- };
- }
- }
- }
-
- break;
- }
- }
-
- GuiItem item = new GuiItem(itemStack, action);
-
- if (element.hasAttribute("field"))
- XMLUtil.loadFieldAttribute(instance, element, item);
-
- if (element.hasAttribute("populate")) {
+ public static void load(@NotNull Pane pane, @NotNull Object instance, @NotNull Element element) {
+ if (element.hasAttribute("priority")) {
try {
- MethodUtils.invokeExactMethod(instance, "populate", item, GuiItem.class);
- } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException exception) {
- throw new XMLLoadException(exception);
+ pane.setPriority(Priority.valueOf(element.getAttribute("priority").toUpperCase()));
+ } catch (IllegalArgumentException exception) {
+ throw new XMLLoadException("Priority attribute is not a proper value", exception);
}
}
-
- item.setProperties(properties);
-
- return item;
- }
-
- public static void load(@NotNull Pane pane, @NotNull Object instance, @NotNull Element element) {
- if (element.hasAttribute("x")) {
- pane.setX(Integer.parseInt(element.getAttribute("x")));
- }
-
- if (element.hasAttribute("y")) {
- pane.setY(Integer.parseInt(element.getAttribute("y")));
- }
-
- if (element.hasAttribute("priority"))
- pane.setPriority(Priority.valueOf(element.getAttribute("priority")));
if (element.hasAttribute("visible"))
pane.setVisible(Boolean.parseBoolean(element.getAttribute("visible")));
@@ -516,6 +230,8 @@ public static void load(@NotNull Pane pane, @NotNull Object instance, @NotNull E
if (element.hasAttribute("populate")) {
String attribute = element.getAttribute("populate");
+ boolean found = false;
+
for (Method method: instance.getClass().getMethods()) {
if (!method.getName().equals(attribute))
continue;
@@ -526,10 +242,38 @@ public static void load(@NotNull Pane pane, @NotNull Object instance, @NotNull E
} catch (IllegalAccessException | InvocationTargetException exception) {
throw new XMLLoadException(exception);
}
+
+ found = true;
+ }
+
+ if (!found) {
+ throw new XMLLoadException("Specified method could not be found");
}
}
}
+ /**
+ * Checks whether a {@link GuiItem} is the same item as the given {@link ItemStack}. The item will be compared using
+ * internal data. When the item does not have this data, this method will return false. If the item does have such
+ * data, but its value does not match, false is also returned. This method will not mutate any of the provided
+ * arguments.
+ *
+ * @param guiItem the gui item to check
+ * @param item the item which the gui item should be checked against
+ * @return true if the {@link GuiItem} matches the {@link ItemStack}, false otherwise
+ * @since 0.10.14
+ */
+ @Contract(pure = true)
+ protected static boolean matchesItem(@NotNull GuiItem guiItem, @NotNull ItemStack item) {
+ ItemMeta meta = item.getItemMeta();
+
+ if (meta == null) {
+ return false;
+ }
+
+ return guiItem.getUUID().equals(meta.getPersistentDataContainer().get(guiItem.getKey(), UUIDTagType.INSTANCE));
+ }
+
/**
* Finds a type of {@link GuiItem} from the provided collection of items based on the provided {@link ItemStack}.
* The items will be compared using internal data. When the item does not have this data, this method will return
@@ -548,19 +292,13 @@ public static void load(@NotNull Pane pane, @NotNull Object instance, @NotNull E
@Nullable
@Contract(pure = true)
protected static T findMatchingItem(@NotNull Collection items, @NotNull ItemStack item) {
- ItemMeta meta = item.getItemMeta();
- if (meta == null) {
- return null;
- }
-
- UUID uuid = meta.getPersistentDataContainer().get(GuiItem.KEY_UUID, UUIDTagType.INSTANCE);
- if (uuid == null) {
- return null;
+ for (T guiItem : items) {
+ if (matchesItem(guiItem, item)) {
+ return guiItem;
+ }
}
- return items.stream()
- .filter(guiItem -> guiItem.getUUID().equals(uuid))
- .findAny().orElse(null);
+ return null;
}
/**
@@ -608,14 +346,13 @@ public Priority getPriority() {
* @param onClick the consumer that gets called
* @since 0.4.0
*/
- public void setOnClick(@Nullable Consumer onClick) {
+ public void setOnClick(@Nullable Consumer super InventoryClickEvent> onClick) {
this.onClick = onClick;
}
/**
* Calls the consumer (if it's not null) that was specified using {@link #setOnClick(Consumer)},
* so the consumer that should be called whenever this pane is clicked in.
- * Catches and logs all exceptions the consumer might throw.
*
* @param event the event to handle
* @since 0.6.0
@@ -624,33 +361,94 @@ protected void callOnClick(@NotNull InventoryClickEvent event) {
if (onClick == null) {
return;
}
-
+
+
try {
onClick.accept(event);
} catch (Throwable t) {
- Logger logger = JavaPlugin.getProvidingPlugin(getClass()).getLogger();
- logger.log(Level.SEVERE, "Exception while handling click event in inventory '"
- + event.getView().getTitle() + "', slot=" + event.getSlot() + ", for "
- + getClass().getSimpleName() + ", x=" + x + ", y=" + y + ", length=" + length + ", height=" + height, t);
+ throw new RuntimeException(
+ "Exception while handling click event in inventory '"
+ + InventoryViewUtil.getInstance().getTitle(event.getView()) + "', slot=" + event.getSlot() +
+ ", for " + getClass().getSimpleName() + ", length=" + length + ", height=" + height,
+ t
+ );
}
}
/**
- * Registers a property that can be used inside an XML file to add additional new properties.
- * The use of {@link Gui#registerProperty(String, Function)} is preferred over this method.
+ * Creates a pane which displays as a border around the outside of the pane consisting of the provided item. The
+ * slot, length and height parameters are used for the respective properties of the pane. If either the length or
+ * height is negative an {@link IllegalArgumentException} will be thrown.
*
- * @param attributeName the name of the property. This is the same name you'll be using to specify the property
- * type in the XML file.
- * @param function how the property should be processed. This converts the raw text input from the XML node value
- * into the correct object type.
- * @throws IllegalArgumentException when a property with this name is already registered.
+ * @param length the length of the pane
+ * @param height the height of the pane
+ * @param item the item of which the border is made
+ * @return the created pane which displays a border
+ * @since 0.12.0
+ * @throws IllegalArgumentException if length or height is negative
*/
- public static void registerProperty(@NotNull String attributeName, @NotNull Function function) {
- if (PROPERTY_MAPPINGS.containsKey(attributeName)) {
- throw new IllegalArgumentException("property '" + attributeName + "' is already registered");
+ @NotNull
+ @Contract(pure = true)
+ public static Pane createBorder(int length, int height, @NotNull GuiItem item) {
+ if (length < 0) {
+ throw new IllegalArgumentException("Length should be non-negative");
}
-
- PROPERTY_MAPPINGS.put(attributeName, function);
+
+ if (height < 0) {
+ throw new IllegalArgumentException("Height should be non-negative");
+ }
+
+ String[] mask = new String[height];
+
+ if (height > 0) {
+ mask[0] = createLine(length);
+ }
+
+ if (height > 1) {
+ mask[height - 1] = createLine(length);
+ }
+
+ for (int yIndex = 1; yIndex < height - 1; yIndex++) {
+ StringBuilder builder = new StringBuilder("1");
+
+ for (int i = 0; i < length - 2; i++) {
+ builder.append('0');
+ }
+
+ mask[yIndex] = builder.append('1').toString();
+ }
+
+ OutlinePane pane = new OutlinePane(length, height);
+ pane.applyMask(new Mask(mask));
+ pane.addItem(item);
+ pane.setRepeat(true);
+
+ return pane;
+ }
+
+ /**
+ * Creates a string containing the character '1' repeated length amount of times. If the provided length is negative
+ * an {@link IllegalArgumentException} will be thrown.
+ *
+ * @param length the length of the string
+ * @return the string containing '1's
+ * @since 0.10.7
+ * @throws IllegalArgumentException if length is negative
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static String createLine(int length) {
+ if (length < 0) {
+ throw new IllegalArgumentException("Length should be non-negative");
+ }
+
+ StringBuilder builder = new StringBuilder();
+
+ for (int i = 0; i < length; i++) {
+ builder.append('1');
+ }
+
+ return builder.toString();
}
/**
@@ -741,16 +539,4 @@ public boolean isGreaterThan(@NotNull Priority priority) {
return !isLessThan(priority) && this != priority;
}
}
-
- static {
- PROPERTY_MAPPINGS.put("boolean", Boolean::parseBoolean);
- PROPERTY_MAPPINGS.put("byte", Byte::parseByte);
- PROPERTY_MAPPINGS.put("character", value -> value.charAt(0));
- PROPERTY_MAPPINGS.put("double", Double::parseDouble);
- PROPERTY_MAPPINGS.put("float", Float::parseFloat);
- PROPERTY_MAPPINGS.put("integer", Integer::parseInt);
- PROPERTY_MAPPINGS.put("long", Long::parseLong);
- PROPERTY_MAPPINGS.put("short", Short::parseShort);
- PROPERTY_MAPPINGS.put("string", value -> value);
- }
}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PatternPane.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PatternPane.java
index 4adde932e..33045ca0d 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PatternPane.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/PatternPane.java
@@ -2,12 +2,15 @@
import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
-import com.github.stefvanschie.inventoryframework.gui.InventoryComponent;
+import com.github.stefvanschie.inventoryframework.gui.GuiComponent;
import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
+import com.github.stefvanschie.inventoryframework.pane.util.GuiItemContainer;
import com.github.stefvanschie.inventoryframework.pane.util.Pattern;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
import com.github.stefvanschie.inventoryframework.util.GeometryUtil;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Element;
@@ -54,21 +57,19 @@ public class PatternPane extends Pane implements Flippable, Rotatable {
/**
* Constructs a new pattern pane.
*
- * @param x the upper left x coordinate of the pane
- * @param y the upper left y coordinate of the pane
* @param length the length of the pane
* @param height the height of the pane
* @param priority the priority of the pane
* @param pattern the pattern of the pane
* @throws IllegalArgumentException when the pane and pattern dimensions don't match
- * @since 0.9.8
+ * @since 0.12.0
*/
- public PatternPane(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Pattern pattern) {
- super(x, y, length, height, priority);
+ public PatternPane(int length, int height, @NotNull Priority priority, @NotNull Pattern pattern) {
+ super(length, height, priority);
if (pattern.getLength() != length || pattern.getHeight() != height) {
throw new IllegalArgumentException(
- "Dimensions of the provided pattern do not match the dimensions of the pane"
+ "Dimensions of the provided pattern do not match the dimensions of the pane"
);
}
@@ -85,32 +86,16 @@ public PatternPane(int x, int y, int length, int height, @NotNull Priority prior
* @since 0.9.8
*/
public PatternPane(int length, int height, @NotNull Pattern pattern) {
- this(0, 0, length, height, pattern);
- }
-
- /**
- * Constructs a new pattern pane.
- *
- * @param x the upper left x coordinate of the pane
- * @param y the upper left y coordinate of the pane
- * @param length the length of the pane
- * @param height the height of the pane
- * @param pattern the pattern of the pane
- * @throws IllegalArgumentException when the pane and pattern dimensions don't match
- * @since 0.9.8
- */
- public PatternPane(int x, int y, int length, int height, @NotNull Pattern pattern) {
- this(x, y, length, height, Priority.NORMAL, pattern);
+ this(length, height, Priority.NORMAL, pattern);
}
+ @NotNull
@Override
- public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
- int length = Math.min(this.length, maxLength);
- int height = Math.min(this.height, maxHeight);
+ public GuiItemContainer display() {
+ GuiItemContainer container = new GuiItemContainer(getLength(), getHeight());
- for (int x = 0; x < length; x++) {
- for (int y = 0; y < height; y++) {
+ for (int x = 0; x < getLength(); x++) {
+ for (int y = 0; y < getHeight(); y++) {
GuiItem item = this.bindings.get(pattern.getCharacter(x, y));
if (item == null || !item.isVisible()) {
@@ -120,41 +105,31 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs
int newX = x, newY = y;
if (isFlippedHorizontally()) {
- newX = length - x - 1;
+ newX = getLength() - x - 1;
}
if (isFlippedVertically()) {
- newY = height - y - 1;
+ newY = getHeight() - y - 1;
}
- Map.Entry coordinates = GeometryUtil.processClockwiseRotation(newX, newY, length,
- height, rotation);
-
- newX = coordinates.getKey();
- newY = coordinates.getValue();
-
- int finalRow = getY() + newY + paneOffsetY;
- int finalColumn = getX() + newX + paneOffsetX;
+ Map.Entry coordinates = GeometryUtil.processClockwiseRotation(newX, newY, getLength(),
+ getHeight(), rotation);
- inventoryComponent.setItem(item, finalColumn, finalRow);
+ container.setItem(item, coordinates.getKey(), coordinates.getValue());
}
}
+
+ return container;
}
@Override
- public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent,
- @NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
- int length = Math.min(this.length, maxLength);
- int height = Math.min(this.height, maxHeight);
-
- int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY);
-
- int x = adjustedSlot % inventoryComponent.getLength();
- int y = adjustedSlot / inventoryComponent.getLength();
+ public boolean click(@NotNull Gui gui, @NotNull GuiComponent guiComponent, @NotNull InventoryClickEvent event,
+ @NotNull Slot slot) {
+ int x = slot.getX(getLength());
+ int y = slot.getY(getLength());
//this isn't our item
- if (x < 0 || x >= length || y < 0 || y >= height) {
+ if (x < 0 || x >= getLength() || y < 0 || y >= getHeight()) {
return false;
}
@@ -181,16 +156,16 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
@Contract(pure = true)
@Override
public PatternPane copy() {
- PatternPane patternPane = new PatternPane(getX(), getY(), getLength(), getHeight(), getPriority(), getPattern());
+ PatternPane patternPane = new PatternPane(getLength(), getHeight(), getPriority(), getPattern());
patternPane.setVisible(isVisible());
patternPane.onClick = onClick;
patternPane.uuid = uuid;
- patternPane.setRotation(getRotation());
- patternPane.flipHorizontally(isFlippedHorizontally());
- patternPane.flipVertically(isFlippedVertically());
+ patternPane.rotation = rotation;
+ patternPane.flippedHorizontally = flippedHorizontally;
+ patternPane.flippedVertically = flippedVertically;
return patternPane;
}
@@ -339,87 +314,104 @@ public int getRotation() {
* Loads a pattern pane from a given element
*
* @param instance the instance class
- * @param element the element
+ * @param element the element
+ * @param plugin the plugin that will own the underlying items
* @return the pattern pane
+ * @since 0.10.8
*/
@NotNull
- public static PatternPane load(@NotNull Object instance, @NotNull Element element) {
- try {
- NodeList childNodes = element.getChildNodes();
-
- Pattern pattern = null;
- Map bindings = new HashMap<>();
+ public static PatternPane load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) {
+ NodeList childNodes = element.getChildNodes();
- for (int i = 0; i < childNodes.getLength(); i++) {
- Node item = childNodes.item(i);
+ Pattern pattern = null;
+ Map bindings = new HashMap<>();
- if (item.getNodeType() != Node.ELEMENT_NODE) {
- continue;
- }
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node item = childNodes.item(i);
- Element child = (Element) item;
- String name = item.getNodeName();
+ if (item.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
- if (name.equals("pattern")) {
- pattern = Pattern.load(child);
- } else if (name.equals("binding")) {
- String character = child.getAttribute("char");
+ Element child = (Element) item;
+ String name = item.getNodeName();
- if (character == null) {
- throw new XMLLoadException("Missing char attribute on binding");
- }
+ if (name.equals("pattern")) {
+ pattern = Pattern.load(child);
+ } else if (name.equals("binding")) {
+ if (!child.hasAttribute("char")) {
+ throw new XMLLoadException("Tag binding is missing char attribute");
+ }
- if (character.codePointCount(0, character.length()) != 1) {
- throw new XMLLoadException("Char attribute doesn't have one character");
- }
+ String character = child.getAttribute("char");
- NodeList children = child.getChildNodes();
- GuiItem guiItem = null;
+ if (character.codePointCount(0, character.length()) != 1) {
+ throw new XMLLoadException("Char attribute doesn't have one character");
+ }
- for (int index = 0; index < children.getLength(); index++) {
- Node guiItemNode = children.item(index);
+ NodeList children = child.getChildNodes();
+ GuiItem guiItem = null;
- if (guiItemNode.getNodeType() != Node.ELEMENT_NODE) {
- continue;
- }
+ for (int index = 0; index < children.getLength(); index++) {
+ Node guiItemNode = children.item(index);
- if (guiItem != null) {
- throw new XMLLoadException("Binding has multiple inner tags, one expected");
- }
+ if (guiItemNode.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
- guiItem = Pane.loadItem(instance, (Element) guiItemNode);
+ if (guiItem != null) {
+ throw new XMLLoadException("Binding has multiple inner tags, one expected");
}
- //guaranteed to only be a single code point
- bindings.put(character.codePoints().toArray()[0], guiItem);
- } else {
- throw new XMLLoadException("Unknown tag " + name + " in pattern pane");
+ guiItem = GuiItem.loadItem(instance, (Element) guiItemNode, plugin);
}
- }
- if (pattern == null) {
- throw new XMLLoadException("Pattern pane doesn't have a pattern");
+ //guaranteed to only be a single code point
+ bindings.put(character.codePoints().toArray()[0], guiItem);
+ } else {
+ throw new XMLLoadException("Unknown tag " + name + " in pattern pane");
}
+ }
- PatternPane patternPane = new PatternPane(
- Integer.parseInt(element.getAttribute("length")),
- Integer.parseInt(element.getAttribute("height")),
- pattern
- );
+ if (pattern == null) {
+ throw new XMLLoadException("Pattern pane doesn't have a pattern");
+ }
- Pane.load(patternPane, instance, element);
- Flippable.load(patternPane, element);
- Rotatable.load(patternPane, element);
+ if (!element.hasAttribute("length")) {
+ throw new XMLLoadException("Pattern pane XML tag does not have the mandatory length attribute");
+ }
- if (!element.hasAttribute("populate")) {
- for (Map.Entry entry : bindings.entrySet()) {
- patternPane.bindItem(entry.getKey(), entry.getValue());
- }
- }
+ if (!element.hasAttribute("height")) {
+ throw new XMLLoadException("Pattern pane XML tag does not have the mandatory height attribute");
+ }
+
+ int length;
+ int height;
- return patternPane;
+ try {
+ length = Integer.parseInt(element.getAttribute("length"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Length attribute is not an integer", exception);
+ }
+
+ try {
+ height = Integer.parseInt(element.getAttribute("height"));
} catch (NumberFormatException exception) {
- throw new XMLLoadException(exception);
+ throw new XMLLoadException("Height attribute is not an integer", exception);
+ }
+
+ PatternPane patternPane = new PatternPane(length, height, pattern);
+
+ Pane.load(patternPane, instance, element);
+ Flippable.load(patternPane, element);
+ Rotatable.load(patternPane, element);
+
+ if (!element.hasAttribute("populate")) {
+ for (Map.Entry entry : bindings.entrySet()) {
+ patternPane.bindItem(entry.getKey(), entry.getValue());
+ }
}
+
+ return patternPane;
}
}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Rotatable.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Rotatable.java
index 8905323c3..630946a7d 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Rotatable.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/Rotatable.java
@@ -1,5 +1,6 @@
package com.github.stefvanschie.inventoryframework.pane;
+import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Element;
@@ -45,7 +46,11 @@ public interface Rotatable {
*/
static void load(@NotNull Rotatable rotatable, @NotNull Element element) {
if (element.hasAttribute("rotation")) {
- rotatable.setRotation(Integer.parseInt(element.getAttribute("rotation")));
+ try {
+ rotatable.setRotation(Integer.parseInt(element.getAttribute("rotation")));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Rotation attribute is not an integer", exception);
+ }
}
}
}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/StaticPane.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/StaticPane.java
index b77249f35..8202cdc8d 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/StaticPane.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/StaticPane.java
@@ -1,12 +1,16 @@
package com.github.stefvanschie.inventoryframework.pane;
-import com.github.stefvanschie.inventoryframework.gui.InventoryComponent;
+import com.github.stefvanschie.inventoryframework.gui.GuiComponent;
import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
+import com.github.stefvanschie.inventoryframework.pane.util.GuiItemContainer;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
import com.github.stefvanschie.inventoryframework.util.GeometryUtil;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -19,15 +23,20 @@
/**
* A pane for static items and stuff. All items will have to be specified a slot, or will be added in the next position.
+ *
+ * This pane allows you to specify the positions of the items either in the form of an x and y coordinate pair or as an
+ * index, in which case the indexing starts from the top left and continues to the right and bottom, with the horizontal
+ * axis taking priority. There are nuances at play with regard to mixing these two types of positioning systems within
+ * the same pane. It's recommended to only use one of these systems per pane and to not mix them.
+ *
*/
public class StaticPane extends Pane implements Flippable, Rotatable {
/**
- * A map of locations inside this pane and their item. The locations are stored in a way where the x coordinate is
- * the key and the y coordinate is the value.
+ * A map of locations inside this pane and their item.
*/
@NotNull
- private final Map, GuiItem> items;
+ private final Map items;
/**
* The clockwise rotation of this pane in degrees
@@ -39,30 +48,41 @@ public class StaticPane extends Pane implements Flippable, Rotatable {
*/
private boolean flipHorizontally, flipVertically;
- public StaticPane(int x, int y, int length, int height, @NotNull Priority priority) {
- super(x, y, length, height, priority);
+ /**
+ * Creates a new static pane.
+ *
+ * @param length the length of the pane
+ * @param height the height of the pane
+ * @param priority the priority of the pane
+ * @since 0.12.0
+ */
+ public StaticPane(int length, int height, @NotNull Priority priority) {
+ super(length, height, priority);
this.items = new HashMap<>(length * height);
}
- public StaticPane(int x, int y, int length, int height) {
- this(x, y, length, height, Priority.NORMAL);
- }
-
+ /**
+ * Creates a new static pane.
+ *
+ * @param length the length of the pane
+ * @param height the height of the pane
+ * @since 0.12.0
+ */
public StaticPane(int length, int height) {
- this(0, 0, length, height);
+ this(length, height, Priority.NORMAL);
}
+ @NotNull
@Override
- public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
- int length = Math.min(this.length, maxLength);
- int height = Math.min(this.height, maxHeight);
+ public GuiItemContainer display() {
+ GuiItemContainer container = new GuiItemContainer(getLength(), getHeight());
items.entrySet().stream().filter(entry -> entry.getValue().isVisible()).forEach(entry -> {
- Map.Entry location = entry.getKey();
+ Slot location = entry.getKey();
- int x = location.getKey(), y = location.getValue();
+ int x = location.getX(getLength());
+ int y = location.getY(getLength());
if (flipHorizontally)
x = length - x - 1;
@@ -70,37 +90,49 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs
if (flipVertically)
y = height - y - 1;
- Map.Entry coordinates = GeometryUtil.processClockwiseRotation(x, y, length, height,
- rotation);
+ Map.Entry coordinates = GeometryUtil.processClockwiseRotation(x, y, getLength(),
+ getHeight(), rotation);
x = coordinates.getKey();
y = coordinates.getValue();
- if (x < 0 || x >= length || y < 0 || y >= height) {
+ if (x < 0 || x >= getLength() || y < 0 || y >= getHeight()) {
return;
}
- GuiItem item = entry.getValue();
-
- int finalRow = getY() + y + paneOffsetY;
- int finalColumn = getX() + x + paneOffsetX;
-
- inventoryComponent.setItem(item, finalColumn, finalRow);
+ container.setItem(entry.getValue(), x, y);
});
+
+ return container;
}
+ /**
+ * Adds a gui item at the specific spot in the pane. If the specified slot is already in use, the previous item will
+ * be overwritten by the new item. This is regardless of the way the slot is specified.
+ *
+ * @param item the item to set
+ * @param slot the position of the item
+ * @since 0.10.8
+ */
+ public void addItem(@NotNull GuiItem item, Slot slot) {
+ int x = slot.getX(getLength());
+ int y = slot.getY(getLength());
+
+ this.items.keySet().removeIf(s -> s.getX(getLength()) == x && s.getY(getLength()) == y);
+
+ this.items.put(slot, item);
+ }
+
/**
- * Adds a gui item at the specific spot in the pane. If the coordinates as specified by the x and y parameters is
- * already occupied, that item will be replaced by the item parameter.
+ * Adds a gui item at the specific spot in the pane. If there is another item specified in terms of x and y
+ * coordinates that are equal to the coordinates of this item, the old item will be overwritten by this item.
*
* @param item the item to set
* @param x the x coordinate of the position of the item
* @param y the y coordinate of the position of the item
*/
public void addItem(@NotNull GuiItem item, int x, int y) {
- items.keySet().removeIf(entry -> entry.getKey() == x && entry.getValue() == y);
-
- items.put(new AbstractMap.SimpleEntry<>(x, y), item);
+ addItem(item, Slot.fromXY(x, y));
}
/**
@@ -113,20 +145,38 @@ public void removeItem(@NotNull GuiItem item) {
items.values().removeIf(guiItem -> guiItem.equals(item));
}
- @Override
- public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent,
- @NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
- int length = Math.min(this.length, maxLength);
- int height = Math.min(this.height, maxHeight);
+ /**
+ * Removes the specified item from the pane. This will remove an item regardless of how the slot was specified. If
+ * there is no item at the specified coordinates, this method will do nothing.
+ *
+ * @param x the x coordinate of the item to remove
+ * @param y the y coordinate of the item to remove
+ * @since 0.10.0
+ * @see #removeItem(Slot)
+ */
+ public void removeItem(int x, int y) {
+ this.items.keySet().removeIf(s -> s.getX(getLength()) == x && s.getY(getLength()) == y);
+ }
- int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY);
+ /**
+ * Removes the specified item from the pane. This will remove an item regardless of how the slot was specified. If
+ * there is no item at the specified coordinates, this method will do nothing.
+ *
+ * @param slot the slot of the item to remove
+ * @since 0.10.8
+ */
+ public void removeItem(@NotNull Slot slot) {
+ removeItem(slot.getX(getLength()), slot.getY(getLength()));
+ }
- int x = adjustedSlot % inventoryComponent.getLength();
- int y = adjustedSlot / inventoryComponent.getLength();
+ @Override
+ public boolean click(@NotNull Gui gui, @NotNull GuiComponent guiComponent, @NotNull InventoryClickEvent event,
+ @NotNull Slot slot) {
+ int x = slot.getX(getLength());
+ int y = slot.getY(getLength());
//this isn't our item
- if (x < 0 || x >= length || y < 0 || y >= height) {
+ if (x < 0 || x >= getLength() || y < 0 || y >= getHeight()) {
return false;
}
@@ -153,12 +203,10 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
@Contract(pure = true)
@Override
public StaticPane copy() {
- StaticPane staticPane = new StaticPane(x, y, length, height, getPriority());
-
- for (Map.Entry, GuiItem> entry : items.entrySet()) {
- Map.Entry coordinates = entry.getKey();
+ StaticPane staticPane = new StaticPane(getLength(), getHeight(), getPriority());
- staticPane.addItem(entry.getValue().copy(), coordinates.getKey(), coordinates.getValue());
+ for (Map.Entry entry : items.entrySet()) {
+ staticPane.addItem(entry.getValue().copy(), entry.getKey());
}
staticPane.setVisible(isVisible());
@@ -189,31 +237,45 @@ public void setRotation(int rotation) {
* Fills all empty space in the pane with the given {@code itemStack} and adds the given action
*
* @param itemStack The {@link ItemStack} to fill the empty space with
- * @param action The action called whenever an interaction with the item happens
- * @since 0.5.9
+ * @param action The action called whenever an interaction with the item happens
+ * @param plugin the plugin that will be the owner of the created items
+ * @see #fillWith(ItemStack, Consumer)
+ * @since 0.10.8
*/
- public void fillWith(@NotNull ItemStack itemStack, @Nullable Consumer action) {
+ public void fillWith(@NotNull ItemStack itemStack, @Nullable Consumer super InventoryClickEvent> action,
+ @NotNull Plugin plugin) {
//The non empty spots
- Set> locations = this.items.keySet();
+ Set locations = this.items.keySet();
for (int y = 0; y < this.getHeight(); y++) {
for (int x = 0; x < this.getLength(); x++) {
boolean found = false;
- for (Map.Entry location : locations) {
- if (location.getKey() == x && location.getValue() == y) {
+ for (Slot location : locations) {
+ if (location.getX(getLength()) == x && location.getY(getLength()) == y) {
found = true;
break;
}
}
if (!found) {
- this.addItem(new GuiItem(itemStack, action), x, y);
+ this.addItem(new GuiItem(itemStack, action, plugin), x, y);
}
}
}
}
+ /**
+ * Fills all empty space in the pane with the given {@code itemStack} and adds the given action
+ *
+ * @param itemStack The {@link ItemStack} to fill the empty space with
+ * @param action The action called whenever an interaction with the item happens
+ * @since 0.5.9
+ */
+ public void fillWith(@NotNull ItemStack itemStack, @Nullable Consumer super InventoryClickEvent> action) {
+ fillWith(itemStack, action, JavaPlugin.getProvidingPlugin(StaticPane.class));
+ }
+
/**
* Fills all empty space in the pane with the given {@code itemStack}
*
@@ -225,12 +287,55 @@ public void fillWith(@NotNull ItemStack itemStack) {
this.fillWith(itemStack, null);
}
+ /**
+ * Gets the item located at the provided slot. If the provided slot is empty, this will return null. The slots are
+ * checked based on their position without regard for their underlying definition. For example, if an item was added
+ * with its slot specified as an x,y coordinate pair, but this method is invoked with a slot specified as an index,
+ * this item may still be returned if the x,y coordinate pair and slot are at the same position, given the current
+ * dimensions of the pane. If multiple items match the position indicated by the provided slot, any of those items
+ * may be the result of this invocation.
+ *
+ * @param slot the slot of the item
+ * @return the item at this position, or null if there is no such item
+ * @since 0.11.4
+ */
+ @Nullable
+ @Contract(pure = true)
+ public GuiItem getItem(@NotNull Slot slot) {
+ int x = slot.getX(getLength());
+ int y = slot.getY(getLength());
+
+ for (Map.Entry entry : this.items.entrySet()) {
+ Slot key = entry.getKey();
+
+ if (key.getX(getLength()) == x && key.getY(getLength()) == y) {
+ return entry.getValue();
+ }
+ }
+
+ return null;
+ }
+
@NotNull
@Override
public Collection getItems() {
return items.values();
}
+ /**
+ * Gets all items by their corresponding slots. The slots correspond to the type they were added with. For example,
+ * if the slot was specified as an x,y coordinate pair, the slot will also be specified as such a pair. The returned
+ * map is unmodifiable.
+ *
+ * @return a map of all items by their slot
+ * @since 0.11.4
+ */
+ @NotNull
+ @Contract(pure = true)
+ public Map<@NotNull Slot, @NotNull GuiItem> getSlottedItems() {
+ return Collections.unmodifiableMap(this.items);
+ }
+
@Override
public void clear() {
items.clear();
@@ -275,41 +380,58 @@ public boolean isFlippedVertically() {
* Loads an outline pane from a given element
*
* @param instance the instance class
- * @param element the element
+ * @param element the element
+ * @param plugin the plugin that will be the owner of the udnerlying items
* @return the outline pane
+ * @since 0.10.8
*/
@NotNull
- public static StaticPane load(@NotNull Object instance, @NotNull Element element) {
- try {
- StaticPane staticPane = new StaticPane(
- Integer.parseInt(element.getAttribute("length")),
- Integer.parseInt(element.getAttribute("height"))
- );
+ public static StaticPane load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) {
+ if (!element.hasAttribute("length")) {
+ throw new XMLLoadException("Cycle button XML tag does not have the mandatory length attribute");
+ }
- Pane.load(staticPane, instance, element);
- Flippable.load(staticPane, element);
- Rotatable.load(staticPane, element);
+ if (!element.hasAttribute("height")) {
+ throw new XMLLoadException("Cycle button XML tag does not have the mandatory height attribute");
+ }
- if (element.hasAttribute("populate"))
- return staticPane;
+ int length;
+ int height;
- NodeList childNodes = element.getChildNodes();
+ try {
+ length = Integer.parseInt(element.getAttribute("length"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Length attribute is not an integer", exception);
+ }
- for (int i = 0; i < childNodes.getLength(); i++) {
- Node item = childNodes.item(i);
+ try {
+ height = Integer.parseInt(element.getAttribute("height"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Height attribute is not an integer", exception);
+ }
- if (item.getNodeType() != Node.ELEMENT_NODE)
- continue;
+ StaticPane staticPane = new StaticPane(length, height);
- Element child = (Element) item;
+ Pane.load(staticPane, instance, element);
+ Flippable.load(staticPane, element);
+ Rotatable.load(staticPane, element);
- staticPane.addItem(Pane.loadItem(instance, child), Integer.parseInt(child.getAttribute("x")),
- Integer.parseInt(child.getAttribute("y")));
- }
+ if (element.hasAttribute("populate"))
+ return staticPane;
- return staticPane;
- } catch (NumberFormatException exception) {
- throw new XMLLoadException(exception);
- }
+ NodeList childNodes = element.getChildNodes();
+
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node item = childNodes.item(i);
+
+ if (item.getNodeType() != Node.ELEMENT_NODE)
+ continue;
+
+ Element child = (Element) item;
+
+ staticPane.addItem(GuiItem.loadItem(instance, child, plugin), Slot.deserialize(child));
+ }
+
+ return staticPane;
}
}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButton.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButton.java
index 709d70284..fede0fbe6 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButton.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButton.java
@@ -1,11 +1,14 @@
package com.github.stefvanschie.inventoryframework.pane.component;
-import com.github.stefvanschie.inventoryframework.gui.InventoryComponent;
+import com.github.stefvanschie.inventoryframework.gui.GuiComponent;
import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
import com.github.stefvanschie.inventoryframework.pane.Pane;
+import com.github.stefvanschie.inventoryframework.pane.util.GuiItemContainer;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Element;
@@ -32,35 +35,42 @@ public class CycleButton extends Pane {
*/
private int position = 0;
- public CycleButton(int x, int y, int length, int height, @NotNull Priority priority) {
- super(x, y, length, height, priority);
- }
-
- public CycleButton(int x, int y, int length, int height) {
- super(x, y, length, height);
+ /**
+ * Creates a new cycle button
+ *
+ * @param length the length of the button
+ * @param height the height of the button
+ * @param priority the priority of the button
+ * @since 0.12.0
+ */
+ public CycleButton(int length, int height, @NotNull Priority priority) {
+ super(length, height, priority);
}
+ /**
+ * Creates a new cycle button
+ *
+ * @param length the length of the button
+ * @param height the height of the button
+ * @since 0.12.0
+ */
public CycleButton(int length, int height) {
super(length, height);
}
@Override
- public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent,
- @NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
- int length = Math.min(this.length, maxLength);
- int height = Math.min(this.height, maxHeight);
-
- int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY);
-
- int x = adjustedSlot % inventoryComponent.getLength();
- int y = adjustedSlot / inventoryComponent.getLength();
+ public boolean click(@NotNull Gui gui, @NotNull GuiComponent guiComponent, @NotNull InventoryClickEvent event,
+ @NotNull Slot slot) {
+ int x = slot.getX(getLength());
+ int y = slot.getY(getLength());
//this isn't our item
- if (x < 0 || x >= length || y < 0 || y >= height) {
+ if (x < 0 || x >= getLength() || y < 0 || y >= getHeight()) {
return false;
}
+ int previousPosition = position;
+
position++;
if (position == panes.size()) {
@@ -69,32 +79,29 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
callOnClick(event);
- Pane pane = panes.get(position);
- pane.click(gui, inventoryComponent, event, slot, paneOffsetX + x, paneOffsetY + y,
- length, height);
+ //use the previous position, since that will have the pane we clicked on
+ panes.get(previousPosition).click(gui, guiComponent, event, slot);
gui.update();
return true;
}
+ @NotNull
@Override
- public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
- int newX = paneOffsetX + x;
- int newY = paneOffsetY + y;
+ public GuiItemContainer display() {
+ GuiItemContainer container = new GuiItemContainer(getLength(), getHeight());
- int newMaxLength = Math.min(maxLength, length);
- int newMaxHeight = Math.min(maxHeight, height);
+ container.apply(this.panes.get(this.position).display(), 0, 0);
- panes.get(position).display(inventoryComponent, newX, newY, newMaxLength, newMaxHeight);
+ return container;
}
@NotNull
@Contract(pure = true)
@Override
public CycleButton copy() {
- CycleButton cycleButton = new CycleButton(x, y, length, height, getPriority());
+ CycleButton cycleButton = new CycleButton(getLength(), getHeight(), getPriority());
for (Pane pane : panes) {
cycleButton.addPane(pane);
@@ -162,19 +169,33 @@ public void cycle() {
*
* @param instance the instance class
* @param element the element
+ * @param plugin the plugin that will be the owner of the underlying items
* @return the cycle button
- * @since 0.5.0
+ * @since 0.10.8
*/
@NotNull
- public static CycleButton load(@NotNull Object instance, @NotNull Element element) {
+ public static CycleButton load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) {
+ if (!element.hasAttribute("length")) {
+ throw new XMLLoadException("Cycle button XML tag does not have the mandatory length attribute");
+ }
+
+ if (!element.hasAttribute("height")) {
+ throw new XMLLoadException("Cycle button XML tag does not have the mandatory height attribute");
+ }
+
int length;
int height;
try {
length = Integer.parseInt(element.getAttribute("length"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Length attribute is not an integer", exception);
+ }
+
+ try {
height = Integer.parseInt(element.getAttribute("height"));
} catch (NumberFormatException exception) {
- throw new XMLLoadException(exception);
+ throw new XMLLoadException("Height attribute is not an integer", exception);
}
CycleButton cycleButton = new CycleButton(length, height);
@@ -194,7 +215,7 @@ public static CycleButton load(@NotNull Object instance, @NotNull Element elemen
continue;
}
- cycleButton.addPane(Gui.loadPane(instance, pane));
+ cycleButton.addPane(Gui.loadPane(instance, pane, plugin));
}
return cycleButton;
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Label.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Label.java
index 205315ad2..6d0fbc157 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Label.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Label.java
@@ -1,23 +1,31 @@
package com.github.stefvanschie.inventoryframework.pane.component;
-import com.github.stefvanschie.inventoryframework.gui.InventoryComponent;
+import com.github.stefvanschie.inventoryframework.gui.GuiComponent;
import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
import com.github.stefvanschie.inventoryframework.font.util.Font;
import com.github.stefvanschie.inventoryframework.pane.*;
+import com.github.stefvanschie.inventoryframework.pane.util.GuiItemContainer;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Element;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.function.BiFunction;
+
/**
* A label for displaying text.
*
* @since 0.5.0
*/
-public class Label extends OutlinePane {
+public class Label extends Pane {
/**
* The character set used for displaying the characters in this label
@@ -31,19 +39,37 @@ public class Label extends OutlinePane {
@NotNull
private String text;
+ /**
+ * The pane used for displaying the label.
+ */
+ @NotNull
+ private OutlinePane pane;
+
+ /**
+ * The plugin to be sed for creating items
+ */
+ @NotNull
+ private final Plugin plugin;
+
/**
* Creates a new label
*
- * @param x the x coordinate
- * @param y the y coordinate
* @param length the length
* @param height the height
* @param priority the priority
* @param font the character set
- * @since 0.5.0
+ * @param plugin the plugin that will be the owner for this label's items
+ * @since 0.12.0
*/
- public Label(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Font font) {
- this(x, y, length, height, font);
+ public Label(int length, int height, @NotNull Priority priority, @NotNull Font font, @NotNull Plugin plugin) {
+ super(length, height);
+
+ this.pane = new OutlinePane(length, height, priority);
+
+ this.font = font;
+ this.text = "";
+
+ this.plugin = plugin;
setPriority(priority);
}
@@ -51,18 +77,27 @@ public Label(int x, int y, int length, int height, @NotNull Priority priority, @
/**
* Creates a new label
*
- * @param x the x coordinate
- * @param y the y coordinate
* @param length the length
* @param height the height
* @param font the character set
- * @since 0.5.0
+ * @param plugin the plugin that will be the owner for this label's items
+ * @since 0.12.0
*/
- public Label(int x, int y, int length, int height, @NotNull Font font) {
- this(length, height, font);
+ public Label(int length, int height, @NotNull Font font, @NotNull Plugin plugin) {
+ this(length, height, Priority.NORMAL, font, plugin);
+ }
- this.x = x;
- this.y = y;
+ /**
+ * Creates a new label
+ *
+ * @param length the length
+ * @param height the height
+ * @param priority the priority
+ * @param font the character set
+ * @since 0.12.0
+ */
+ public Label(int length, int height, @NotNull Priority priority, @NotNull Font font) {
+ this(length, height, priority, font, JavaPlugin.getProvidingPlugin(Label.class));
}
/**
@@ -71,22 +106,25 @@ public Label(int x, int y, int length, int height, @NotNull Font font) {
* @param length the length
* @param height the height
* @param font the character set
- * @since 0.5.0
+ * @since 0.12.0
*/
public Label(int length, int height, @NotNull Font font) {
- super(length, height);
-
- this.font = font;
- this.text = "";
+ this(length, height, Priority.NORMAL, font);
}
/**
- * Sets the text to be displayed in this label
+ * Sets the text to be displayed in this label. If this label already had text, this text will be overwritten. The
+ * specified processor will be called for each character that is part of the specified text. The provided character
+ * will be the original character that was attempted to be shown - it is not subject to any transformations that may
+ * be applied for finding a valid item corresponding to this character, such as capitalization changes.
*
* @param text the new text
- * @since 0.5.0
+ * @param processor processes each character before using them
+ * @since 0.10.4
*/
- public void setText(@NotNull String text) {
+ public void setText(@NotNull String text,
+ @NotNull BiFunction super @NotNull Character, ? super @NotNull ItemStack,
+ ? extends @NotNull GuiItem> processor) {
this.text = text;
clear();
@@ -106,30 +144,31 @@ public void setText(@NotNull String text) {
item = font.getDefaultItem();
}
- addItem(new GuiItem(item));
+ this.pane.addItem(processor.apply(character, item.clone()));
}
}
+ /**
+ * Sets the text to be displayed in this label. If this label already had text, this text will be overwritten.
+ *
+ * @param text the new text
+ * @see #setText(String, BiFunction)
+ * @since 0.5.0
+ */
+ public void setText(@NotNull String text) {
+ setText(text, (character, item) -> new GuiItem(item, this.plugin));
+ }
+
@NotNull
@Contract(pure = true)
@Override
public Label copy() {
- Label label = new Label(x, y, length, height, getPriority(), font);
-
- for (GuiItem item : getItems()) {
- label.addItem(item.copy());
- }
+ Label label = new Label(getLength(), getHeight(), getPriority(), getFont(), this.plugin);
label.setVisible(isVisible());
label.onClick = onClick;
- label.setOrientation(getOrientation());
- label.setRotation(getRotation());
- label.setGap(getGap());
- label.setRepeat(doesRepeat());
- label.flipHorizontally(isFlippedHorizontally());
- label.flipVertically(isFlippedVertically());
- label.applyMask(getMask());
+ label.pane = this.pane.copy();
label.uuid = uuid;
label.text = text;
@@ -138,12 +177,34 @@ public Label copy() {
}
@Override
- public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent,
- @NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
+ public boolean click(@NotNull Gui gui, @NotNull GuiComponent guiComponent, @NotNull InventoryClickEvent event,
+ @NotNull Slot slot) {
event.setCancelled(true);
- return super.click(gui, inventoryComponent, event, slot, paneOffsetX, paneOffsetY, maxLength, maxHeight);
+ return this.pane.click(gui, guiComponent, event, slot);
+ }
+
+ @NotNull
+ @Override
+ public GuiItemContainer display() {
+ return this.pane.display();
+ }
+
+ @NotNull
+ @Override
+ public Collection getItems() {
+ return this.pane.getItems();
+ }
+
+ @Override
+ public void clear() {
+ this.pane.clear();
+ }
+
+ @NotNull
+ @Override
+ public Collection getPanes() {
+ return Collections.emptySet();
}
/**
@@ -174,20 +235,35 @@ public Font getFont() {
* Loads a label from a given element
*
* @param instance the instance class
- * @param element the element
+ * @param element the element
+ * @param plugin the plugin that will be the owner of the underlying items
* @return the percentage bar
+ * @since 0.10.8
*/
@NotNull
@Contract(pure = true)
- public static Label load(@NotNull Object instance, @NotNull Element element) {
+ public static Label load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) {
+ if (!element.hasAttribute("length")) {
+ throw new XMLLoadException("Label XML tag does not have the mandatory length attribute");
+ }
+
+ if (!element.hasAttribute("height")) {
+ throw new XMLLoadException("Label XML tag does not have the mandatory height attribute");
+ }
+
int length;
int height;
try {
length = Integer.parseInt(element.getAttribute("length"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Length attribute is not an integer", exception);
+ }
+
+ try {
height = Integer.parseInt(element.getAttribute("height"));
} catch (NumberFormatException exception) {
- throw new XMLLoadException(exception);
+ throw new XMLLoadException("Height attribute is not an integer", exception);
}
Font font = null;
@@ -200,12 +276,9 @@ public static Label load(@NotNull Object instance, @NotNull Element element) {
throw new XMLLoadException("Incorrect font specified for label");
}
- Label label = new Label(length, height, font);
+ Label label = new Label(length, height, font, plugin);
Pane.load(label, instance, element);
- Orientable.load(label, element);
- Flippable.load(label, element);
- Rotatable.load(label, element);
if (element.hasAttribute("populate")) {
return label;
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PagingButtons.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PagingButtons.java
new file mode 100644
index 000000000..c37907866
--- /dev/null
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PagingButtons.java
@@ -0,0 +1,324 @@
+package com.github.stefvanschie.inventoryframework.pane.component;
+
+import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
+import com.github.stefvanschie.inventoryframework.gui.GuiComponent;
+import com.github.stefvanschie.inventoryframework.gui.GuiItem;
+import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
+import com.github.stefvanschie.inventoryframework.pane.PaginatedPane;
+import com.github.stefvanschie.inventoryframework.pane.Pane;
+import com.github.stefvanschie.inventoryframework.pane.util.GuiItemContainer;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
+import org.bukkit.Material;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.w3c.dom.Element;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+
+/**
+ * An interface for interacting with {@link PaginatedPane}s. This gives two buttons for navigating backwards and
+ * forwards through the pages of the {@link PaginatedPane}. The backward button will be displayed at (0, 0) of this pane
+ * and the forward button will be displayed at (length - 1, 0) of this pane. If the paginated pane is at the first page
+ * or the last page, the backwards respectively the forward button will not show. This does not display the
+ * {@link PaginatedPane} itself, but is merely an interface for interacting with it.
+ *
+ * @since 0.10.14
+ */
+public class PagingButtons extends Pane {
+
+ /**
+ * The paginated pane.
+ */
+ @NotNull
+ private final PaginatedPane pages;
+
+ /**
+ * The backwards button.
+ */
+ @NotNull
+ private GuiItem backwardButton;
+
+ /**
+ * The forwards button.
+ */
+ @NotNull
+ private GuiItem forwardButton;
+
+ /**
+ * Whether to keep the backward/forward button always visible
+ */
+ private boolean keepButtonsVisible;
+
+ /**
+ * The plugin with which the items were created.
+ */
+ @NotNull
+ private final Plugin plugin;
+
+ /**
+ * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward
+ * item will be an arrow. If the length provided is less than 2, this will throw an
+ * {@link IllegalArgumentException}.
+ *
+ * @param length the length of this interface
+ * @param priority the priority of this interface
+ * @param pages the pages to interact with
+ * @param plugin the plugin that will be the owner of this interface's items
+ * @since 0.12.0
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(int length, @NotNull Priority priority, @NotNull PaginatedPane pages, @NotNull Plugin plugin) {
+ super(length, 1, priority);
+
+ if (length < 2) {
+ throw new IllegalArgumentException("Length of paging buttons must be at least 2");
+ }
+
+ this.pages = pages;
+ this.plugin = plugin;
+
+ this.backwardButton = new GuiItem(new ItemStack(Material.ARROW), plugin);
+ this.forwardButton = new GuiItem(new ItemStack(Material.ARROW), plugin);
+ this.keepButtonsVisible = false;
+ }
+
+ /**
+ * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward
+ * item will be an arrow. If the length provided is less than 2, this will throw an
+ * {@link IllegalArgumentException}.
+ *
+ * @param length the length of this interface
+ * @param priority the priority of this interface
+ * @param pages the pages to interact with
+ * @since 0.12.0
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(int length, @NotNull Priority priority, @NotNull PaginatedPane pages) {
+ this(length, priority, pages, JavaPlugin.getProvidingPlugin(PagingButtons.class));
+ }
+
+ /**
+ * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward
+ * item will be an arrow. If the length provided is less than 2, this will throw an
+ * {@link IllegalArgumentException}.
+ *
+ * @param length the length of this interface
+ * @param pages the pages to interact with
+ * @param plugin the plugin that will be the owner of this interface's items
+ * @since 0.12.0
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(int length, @NotNull PaginatedPane pages, @NotNull Plugin plugin) {
+ this(length, Priority.NORMAL, pages, plugin);
+ }
+
+ /**
+ * Creates a new PagingButtons instance, which controls the provided {@link PaginatedPane}. The backward and forward
+ * item will be an arrow. If the length provided is less than 2, this will throw an
+ * {@link IllegalArgumentException}.
+ *
+ * @param length the length of this interface
+ * @param pages the pages to interact with
+ * @since 0.12.0
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(int length, @NotNull PaginatedPane pages) {
+ this(length, Priority.NORMAL, pages);
+ }
+
+ @Override
+ public boolean click(@NotNull Gui gui, @NotNull GuiComponent guiComponent, @NotNull InventoryClickEvent event,
+ @NotNull Slot slot) {
+ int x = slot.getX(getLength());
+ int y = slot.getY(getLength());
+
+ //this isn't our item
+ if (x < 0 || x >= getLength() || y < 0 || y >= getHeight()) {
+ return false;
+ }
+
+ callOnClick(event);
+
+ ItemStack itemStack = event.getCurrentItem();
+
+ if (itemStack == null) {
+ return false;
+ }
+
+ if (matchesItem(this.backwardButton, itemStack)) {
+ try {
+ this.pages.setPage(this.pages.getPage() - 1);
+
+ this.backwardButton.callAction(event);
+
+ gui.update();
+ } catch (ArrayIndexOutOfBoundsException ignored) {}
+
+ return true;
+ }
+
+ if (matchesItem(this.forwardButton, itemStack)) {
+ try {
+ this.pages.setPage(this.pages.getPage() + 1);
+
+ this.forwardButton.callAction(event);
+
+ gui.update();
+ } catch (ArrayIndexOutOfBoundsException ignored) {}
+
+ return true;
+ }
+
+ return false;
+ }
+
+ @NotNull
+ @Override
+ public GuiItemContainer display() {
+ GuiItemContainer container = new GuiItemContainer(getLength(), getHeight());
+
+ if (this.keepButtonsVisible || this.pages.getPage() > 0) {
+ container.setItem(this.backwardButton, 0, 0);
+ }
+
+ if (this.keepButtonsVisible || this.pages.getPage() < this.pages.getPages() - 1) {
+ container.setItem(this.forwardButton, getLength() - 1, 0);
+ }
+
+ return container;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This does not make a copy of the {@link PaginatedPane} that is being controlled by this interface.
+ */
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public PagingButtons copy() {
+ PagingButtons pagingButtons = new PagingButtons(getLength(), getPriority(), this.pages, this.plugin);
+
+ pagingButtons.setVisible(isVisible());
+ pagingButtons.onClick = super.onClick;
+
+ pagingButtons.uuid = super.uuid;
+
+ pagingButtons.backwardButton = this.backwardButton.copy();
+ pagingButtons.forwardButton = this.forwardButton.copy();
+
+ return pagingButtons;
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Collection getItems() {
+ Collection items = new HashSet<>();
+
+ items.add(this.backwardButton);
+ items.add(this.forwardButton);
+
+ return Collections.unmodifiableCollection(items);
+ }
+
+ /**
+ * Sets the item to be used for navigating backwards. If an event is attached to the item, this event will be called
+ * after the page has been changed.
+ *
+ * @param item the new backward item
+ * @since 0.10.14
+ */
+ public void setBackwardButton(@NotNull GuiItem item) {
+ this.backwardButton = item;
+ }
+
+ /**
+ * Sets the item to be used for navigating forwards. If an event is attached to the item, this event will be called
+ * after the page has been changed.
+ *
+ * @param item the new forward item
+ * @since 0.10.14
+ */
+ public void setForwardButton(@NotNull GuiItem item) {
+ this.forwardButton = item;
+ }
+
+ /**
+ * Allow to always keep the backward and forward buttons visible when on the first and last page
+ *
+ * @param visible Whether to keep the buttons visible
+ * @since 0.11.6
+ */
+ public void setButtonsAlwaysVisible(boolean visible) {
+ this.keepButtonsVisible = visible;
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Collection getPanes() {
+ return Collections.emptySet();
+ }
+
+ /**
+ * This is a no-op.
+ *
+ * @since 0.10.14
+ */
+ @Override
+ public void clear() {}
+
+ /**
+ * Loads a paging buttons pane from an XML element.
+ *
+ * @param instance the instance class
+ * @param element the element
+ * @param plugin the plugin that will be the owner of the underlying items
+ * @return the paging buttons pane
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static PagingButtons load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) {
+ if (!element.hasAttribute("length")) {
+ throw new XMLLoadException("Paging buttons XML tag does not have the mandatory length attribute");
+ }
+
+ int length;
+
+ try {
+ length = Integer.parseInt(element.getAttribute("length"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Length attribute is not an integer", exception);
+ }
+
+ if (!element.hasAttribute("pages")) {
+ throw new XMLLoadException("Paging buttons does not have pages attribute");
+ }
+
+ Element paginatedPaneElement = element.getOwnerDocument().getElementById(element.getAttribute("pages"));
+
+ if (paginatedPaneElement == null) {
+ throw new XMLLoadException("Paging buttons pages reference is invalid");
+ }
+
+ Object paginatedPane = paginatedPaneElement.getUserData("pane");
+
+ if (!(paginatedPane instanceof PaginatedPane)) {
+ throw new XMLLoadException("Retrieved data is not a paginated pane");
+ }
+
+ PagingButtons pagingButtons = new PagingButtons(length, (PaginatedPane) paginatedPane);
+
+ Pane.load(pagingButtons, instance, element);
+
+ return pagingButtons;
+ }
+}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PercentageBar.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PercentageBar.java
index 49340a8cd..edbd73ce4 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PercentageBar.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PercentageBar.java
@@ -1,13 +1,15 @@
package com.github.stefvanschie.inventoryframework.pane.component;
-import com.github.stefvanschie.inventoryframework.gui.InventoryComponent;
+import com.github.stefvanschie.inventoryframework.gui.GuiComponent;
import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
import com.github.stefvanschie.inventoryframework.pane.Flippable;
import com.github.stefvanschie.inventoryframework.pane.Orientable;
import com.github.stefvanschie.inventoryframework.pane.Pane;
import com.github.stefvanschie.inventoryframework.pane.component.util.VariableBar;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Element;
@@ -19,31 +21,61 @@
*/
public class PercentageBar extends VariableBar {
- public PercentageBar(int x, int y, int length, int height, @NotNull Priority priority) {
- super(x, y, length, height, priority);
+ /**
+ * Creates a new percentage bar
+ *
+ * @param length the length of the bar
+ * @param height the height of the bar
+ * @param priority the priority of the bar
+ * @param plugin the plugin that will be the owner for this percentage bar's items
+ * @since 0.12.0
+ */
+ public PercentageBar(int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) {
+ super(length, height, priority, plugin);
}
- public PercentageBar(int x, int y, int length, int height) {
- super(x, y, length, height);
+ /**
+ * Creates a new percentage bar
+ *
+ * @param length the length of the bar
+ * @param height the height of the bar
+ * @param plugin the plugin that will be the owner for this percentage bar's items
+ * @since 0.12.0
+ */
+ public PercentageBar(int length, int height, @NotNull Plugin plugin) {
+ super(length, height, plugin);
}
+ /**
+ * Creates a new percentage bar
+ *
+ * @param length the length of the bar
+ * @param height the height of the bar
+ * @param priority the priority of the bar
+ * @since 0.12.0
+ */
+ public PercentageBar(int length, int height, @NotNull Priority priority) {
+ super(length, height, priority);
+ }
+
+ /**
+ * Creates a new percentage bar
+ *
+ * @param length the length of the bar
+ * @param height the height of the bar
+ * @since 0.12.0
+ */
public PercentageBar(int length, int height) {
super(length, height);
}
@Override
- public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent,
- @NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
- int length = Math.min(this.length, maxLength);
- int height = Math.min(this.height, maxHeight);
-
- int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY);
+ public boolean click(@NotNull Gui gui, @NotNull GuiComponent guiComponent, @NotNull InventoryClickEvent event,
+ @NotNull Slot slot) {
+ int x = slot.getX(getLength());
+ int y = slot.getY(getLength());
- int x = adjustedSlot % inventoryComponent.getLength();
- int y = adjustedSlot / inventoryComponent.getLength();
-
- if (x < 0 || x >= length || y < 0 || y >= height) {
+ if (x < 0 || x >= getLength() || y < 0 || y >= getHeight()) {
return false;
}
@@ -51,15 +83,11 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
event.setCancelled(true);
- int newPaneOffsetX = paneOffsetX + getX();
- int newPaneOffsetY = paneOffsetY + getY();
-
+ if (this.fillPane.click(gui, guiComponent, event, slot)) {
+ return true;
+ }
- return this.fillPane.click(
- gui, inventoryComponent, event, slot, newPaneOffsetX, newPaneOffsetY, length, height
- ) || this.backgroundPane.click(
- gui, inventoryComponent, event, slot, newPaneOffsetX, newPaneOffsetY, length, height
- );
+ return this.backgroundPane.click(gui, guiComponent, event, slot);
}
/**
@@ -79,7 +107,7 @@ public void setPercentage(float percentage) {
@Contract(pure = true)
@Override
public PercentageBar copy() {
- PercentageBar percentageBar = new PercentageBar(x, y, length, height, getPriority());
+ PercentageBar percentageBar = new PercentageBar(getLength(), getHeight(), getPriority());
applyContents(percentageBar);
@@ -100,23 +128,38 @@ public float getPercentage() {
* Loads a percentage bar from a given element
*
* @param instance the instance class
- * @param element the element
+ * @param element the element
+ * @param plugin the plugin that will be the owner of the underlying items
* @return the percentage bar
+ * @since 0.10.8
*/
@NotNull
@Contract(pure = true)
- public static PercentageBar load(@NotNull Object instance, @NotNull Element element) {
+ public static PercentageBar load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) {
+ if (!element.hasAttribute("length")) {
+ throw new XMLLoadException("Percentage bar XML tag does not have the mandatory length attribute");
+ }
+
+ if (!element.hasAttribute("height")) {
+ throw new XMLLoadException("Percentage bar XML tag does not have the mandatory height attribute");
+ }
+
int length;
int height;
try {
length = Integer.parseInt(element.getAttribute("length"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Length attribute is not an integer", exception);
+ }
+
+ try {
height = Integer.parseInt(element.getAttribute("height"));
} catch (NumberFormatException exception) {
- throw new XMLLoadException(exception);
+ throw new XMLLoadException("Height attribute is not an integer", exception);
}
- PercentageBar percentageBar = new PercentageBar(length, height);
+ PercentageBar percentageBar = new PercentageBar(length, height, plugin);
Pane.load(percentageBar, instance, element);
Orientable.load(percentageBar, element);
@@ -130,7 +173,7 @@ public static PercentageBar load(@NotNull Object instance, @NotNull Element elem
try {
percentageBar.setPercentage(Float.parseFloat(element.getAttribute("percentage")));
} catch (IllegalArgumentException exception) {
- throw new XMLLoadException(exception);
+ throw new XMLLoadException("Percentage attribute is not a float", exception);
}
}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Slider.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Slider.java
index 17e873ff7..addd1d011 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Slider.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/Slider.java
@@ -1,13 +1,15 @@
package com.github.stefvanschie.inventoryframework.pane.component;
-import com.github.stefvanschie.inventoryframework.gui.InventoryComponent;
+import com.github.stefvanschie.inventoryframework.gui.GuiComponent;
import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
import com.github.stefvanschie.inventoryframework.pane.Flippable;
import com.github.stefvanschie.inventoryframework.pane.Orientable;
import com.github.stefvanschie.inventoryframework.pane.Pane;
import com.github.stefvanschie.inventoryframework.pane.component.util.VariableBar;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Element;
@@ -19,31 +21,61 @@
*/
public class Slider extends VariableBar {
- public Slider(int x, int y, int length, int height, @NotNull Priority priority) {
- super(x, y, length, height, priority);
+ /**
+ * Creates a new slider
+ *
+ * @param length the length of the slider
+ * @param height the height of the slider
+ * @param priority the priority of the slider
+ * @param plugin the plugin that will be the owner of the slider's items
+ * @since 0.12.0
+ */
+ public Slider(int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) {
+ super(length, height, priority, plugin);
}
- public Slider(int x, int y, int length, int height) {
- super(x, y, length, height);
+ /**
+ * Creates a new slider
+ *
+ * @param length the length of the slider
+ * @param height the height of the slider
+ * @param plugin the plugin that will be the owner of the slider's items
+ * @since 0.12.0
+ */
+ public Slider(int length, int height, @NotNull Plugin plugin) {
+ super(length, height, plugin);
}
+ /**
+ * Creates a new slider
+ *
+ * @param length the length of the slider
+ * @param height the height of the slider
+ * @param priority the priority of the slider
+ * @since 0.12.0
+ */
+ public Slider(int length, int height, @NotNull Priority priority) {
+ super(length, height, priority);
+ }
+
+ /**
+ * Creates a new slider
+ *
+ * @param length the length of the slider
+ * @param height the height of the slider
+ * @since 0.12.0
+ */
public Slider(int length, int height) {
super(length, height);
}
@Override
- public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent,
- @NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
- int length = Math.min(this.length, maxLength);
- int height = Math.min(this.height, maxHeight);
-
- int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY);
-
- int x = adjustedSlot % inventoryComponent.getLength();
- int y = adjustedSlot / inventoryComponent.getLength();
+ public boolean click(@NotNull Gui gui, @NotNull GuiComponent guiComponent, @NotNull InventoryClickEvent event,
+ @NotNull Slot slot) {
+ int x = slot.getX(getLength());
+ int y = slot.getY(getLength());
- if (x < 0 || x >= length || y < 0 || y >= height) {
+ if (x < 0 || x >= getLength() || y < 0 || y >= getHeight()) {
return false;
}
@@ -57,14 +89,11 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
callOnClick(event);
- int newPaneOffsetX = paneOffsetX + getX();
- int newPaneOffsetY = paneOffsetY + getY();
+ boolean success = this.fillPane.click(gui, guiComponent, event, slot);
- boolean success = this.fillPane.click(
- gui, inventoryComponent, event, slot, newPaneOffsetX, newPaneOffsetY, length, height
- ) || this.backgroundPane.click(
- gui, inventoryComponent, event, slot, newPaneOffsetX, newPaneOffsetY, length, height
- );
+ if (!success) {
+ success = this.backgroundPane.click(gui, guiComponent, event, slot);
+ }
gui.update();
@@ -88,7 +117,7 @@ public void setValue(float value) {
@Contract(pure = true)
@Override
public Slider copy() {
- Slider slider = new Slider(x, y, length, height, getPriority());
+ Slider slider = new Slider(getLength(), getHeight(), getPriority());
applyContents(slider);
@@ -109,23 +138,38 @@ public float getValue() {
* Loads a percentage bar from a given element
*
* @param instance the instance class
- * @param element the element
+ * @param element the element
+ * @param plugin the plugin that will be the owner of the udnerlying items
* @return the percentage bar
+ * @since 0.10.8
*/
@NotNull
@Contract(pure = true)
- public static Slider load(@NotNull Object instance, @NotNull Element element) {
+ public static Slider load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) {
+ if (!element.hasAttribute("length")) {
+ throw new XMLLoadException("Slider XML tag does not have the mandatory length attribute");
+ }
+
+ if (!element.hasAttribute("height")) {
+ throw new XMLLoadException("Slider XML tag does not have the mandatory height attribute");
+ }
+
int length;
int height;
try {
length = Integer.parseInt(element.getAttribute("length"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Length attribute is not an integer", exception);
+ }
+
+ try {
height = Integer.parseInt(element.getAttribute("height"));
} catch (NumberFormatException exception) {
- throw new XMLLoadException(exception);
+ throw new XMLLoadException("Height attribute is not an integer", exception);
}
- Slider slider = new Slider(length, height);
+ Slider slider = new Slider(length, height, plugin);
Pane.load(slider, instance, element);
Orientable.load(slider, element);
@@ -139,7 +183,7 @@ public static Slider load(@NotNull Object instance, @NotNull Element element) {
try {
slider.setValue(Float.parseFloat(element.getAttribute("value")));
} catch (IllegalArgumentException exception) {
- throw new XMLLoadException(exception);
+ throw new XMLLoadException("Value attribute is not a float", exception);
}
}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/ToggleButton.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/ToggleButton.java
index da25e0435..6e168430e 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/ToggleButton.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/ToggleButton.java
@@ -1,221 +1,347 @@
-package com.github.stefvanschie.inventoryframework.pane.component;
-
-import com.github.stefvanschie.inventoryframework.gui.InventoryComponent;
-import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
-import com.github.stefvanschie.inventoryframework.gui.GuiItem;
-import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
-import com.github.stefvanschie.inventoryframework.pane.OutlinePane;
-import com.github.stefvanschie.inventoryframework.pane.Pane;
-import org.bukkit.Material;
-import org.bukkit.event.inventory.InventoryClickEvent;
-import org.bukkit.inventory.ItemStack;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.w3c.dom.Element;
-
-import java.util.Collection;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- * A button that toggles between an enabled and disabled state.
- *
- * @since 0.5.0
- */
-public class ToggleButton extends Pane {
-
- /**
- * The panes used for showing the enabled and disabled states
- */
- private final OutlinePane enabledPane, disabledPane;
-
- /**
- * Whether the button is enabled or disabled
- */
- private boolean enabled = false;
-
- public ToggleButton(int x, int y, int length, int height, @NotNull Priority priority) {
- this(x, y, length, height);
-
- setPriority(priority);
- }
-
- public ToggleButton(int length, int height) {
- super(length, height);
-
- this.enabledPane = new OutlinePane(0, 0, length, height);
- this.enabledPane.addItem(new GuiItem(new ItemStack(Material.GREEN_STAINED_GLASS_PANE)));
- this.enabledPane.setRepeat(true);
-
- this.disabledPane = new OutlinePane(0, 0, length, height);
- this.disabledPane.addItem(new GuiItem(new ItemStack(Material.RED_STAINED_GLASS_PANE)));
- this.disabledPane.setRepeat(true);
- }
-
- public ToggleButton(int x, int y, int length, int height) {
- this(length, height);
-
- setX(x);
- setY(y);
- }
-
- @Override
- public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
- int newX = paneOffsetX + x;
- int newY = paneOffsetY + y;
-
- int newMaxLength = Math.min(maxLength, length);
- int newMaxHeight = Math.min(maxHeight, height);
-
- if (enabled) {
- enabledPane.display(inventoryComponent, newX, newY, newMaxLength, newMaxHeight);
- } else {
- disabledPane.display(inventoryComponent, newX, newY, newMaxLength, newMaxHeight);
- }
- }
-
- @Override
- public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent,
- @NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
- int length = Math.min(this.length, maxLength);
- int height = Math.min(this.height, maxHeight);
-
- int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY);
-
- int x = adjustedSlot % inventoryComponent.getLength();
- int y = adjustedSlot / inventoryComponent.getLength();
-
- //this isn't our item
- if (x < 0 || x >= length || y < 0 || y >= height) {
- return false;
- }
-
- toggle();
-
- callOnClick(event);
-
- int newX = paneOffsetX + x;
- int newY = paneOffsetY + y;
-
- if (enabled) {
- enabledPane.click(gui, inventoryComponent, event, slot, newX, newY, length, height);
- } else {
- disabledPane.click(gui, inventoryComponent, event, slot, newX, newY, length, height);
- }
-
- gui.update();
-
- return true;
- }
-
- @NotNull
- @Contract(pure = true)
- @Override
- public ToggleButton copy() {
- ToggleButton toggleButton = new ToggleButton(x, y, length, height, getPriority());
-
- toggleButton.setVisible(isVisible());
- toggleButton.onClick = onClick;
-
- toggleButton.uuid = uuid;
-
- toggleButton.setEnabledItem(enabledPane.getItems().get(0).copy());
- toggleButton.setDisabledItem(disabledPane.getItems().get(0).copy());
-
- toggleButton.enabled = enabled;
-
- return toggleButton;
- }
-
- /**
- * Sets the item to use when the button is set to disabled
- *
- * @param item the disabled item
- * @since 0.5.0
- */
- public void setDisabledItem(@NotNull GuiItem item) {
- disabledPane.clear();
-
- disabledPane.addItem(item);
- }
-
- /**
- * Sets the item to use when the button is set to enabled
- *
- * @param item the enabled item
- * @since 0.5.0
- */
- public void setEnabledItem(@NotNull GuiItem item) {
- enabledPane.clear();
-
- enabledPane.addItem(item);
- }
-
- @NotNull
- @Override
- public Collection getItems() {
- return getPanes().stream().flatMap(pane -> pane.getItems().stream()).collect(Collectors.toSet());
- }
-
- @NotNull
- @Override
- public Collection getPanes() {
- return Stream.of(enabledPane, disabledPane).collect(Collectors.toSet());
- }
-
- /**
- * Gets whether this toggle button is currently enabled or disabled.
- *
- * @return whether the button is enabled or disabled
- * @since 0.9.6
- */
- @Contract(pure = true)
- public boolean isEnabled() {
- return enabled;
- }
-
- /**
- * Toggles between the enabled and disabled states
- *
- * @since 0.5.0
- */
- public void toggle() {
- enabled = !enabled;
- }
-
- @Override
- public void clear() {}
-
- /**
- * Loads a toggle button from an XML element
- *
- * @param instance the instance class
- * @param element the element
- * @return the toggle button
- * @since 0.5.0
- */
- @NotNull
- @Contract(pure = true)
- public static ToggleButton load(@NotNull Object instance, @NotNull Element element) {
- int length, height;
-
- try {
- length = Integer.parseInt(element.getAttribute("length"));
- height = Integer.parseInt(element.getAttribute("height"));
- } catch (NumberFormatException exception) {
- throw new XMLLoadException(exception);
- }
-
- ToggleButton toggleButton = new ToggleButton(length, height);
-
- Pane.load(toggleButton, instance, element);
-
- if (element.hasAttribute("enabled") && Boolean.parseBoolean(element.getAttribute("enabled"))) {
- toggleButton.toggle();
- }
-
- return toggleButton;
- }
-}
+package com.github.stefvanschie.inventoryframework.pane.component;
+
+import com.github.stefvanschie.inventoryframework.gui.GuiComponent;
+import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
+import com.github.stefvanschie.inventoryframework.gui.GuiItem;
+import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
+import com.github.stefvanschie.inventoryframework.pane.OutlinePane;
+import com.github.stefvanschie.inventoryframework.pane.Pane;
+import com.github.stefvanschie.inventoryframework.pane.util.GuiItemContainer;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
+import org.bukkit.Material;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.w3c.dom.Element;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * A button that toggles between an enabled and disabled state.
+ *
+ * @since 0.5.0
+ */
+public class ToggleButton extends Pane {
+
+ /**
+ * The panes used for showing the enabled and disabled states
+ */
+ private final OutlinePane enabledPane, disabledPane;
+
+ /**
+ * Whether the button is enabled or disabled
+ */
+ private boolean enabled;
+
+ /**
+ * Whether this button can be toggled by a player
+ */
+ private boolean allowToggle = true;
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param length the length
+ * @param height the height
+ * @param priority the priority
+ * @param enabled whether the button should start in its enabled or disabled state
+ * @param plugin the plugin that will be the owner of this button's items
+ * @since 0.12.0
+ */
+ public ToggleButton(int length, int height, @NotNull Priority priority, boolean enabled, @NotNull Plugin plugin) {
+ super(length, height, priority);
+
+ this.enabled = enabled;
+
+ this.enabledPane = new OutlinePane(length, height);
+ this.enabledPane.addItem(new GuiItem(new ItemStack(Material.GREEN_STAINED_GLASS_PANE), plugin));
+ this.enabledPane.setRepeat(true);
+
+ this.disabledPane = new OutlinePane(length, height);
+ this.disabledPane.addItem(new GuiItem(new ItemStack(Material.RED_STAINED_GLASS_PANE), plugin));
+ this.disabledPane.setRepeat(true);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param length the length
+ * @param height the height
+ * @param priority the priority
+ * @param plugin the plugin that will be the owner of this button's items
+ * @since 0.10.8
+ */
+ public ToggleButton(int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) {
+ this(length, height, priority, false, plugin);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param length the length
+ * @param height the height
+ * @param enabled whether the button should start in its enabled or disabled state
+ * @param plugin the plugin that will be the owner of this button's items
+ * @since 0.12.0
+ */
+ public ToggleButton(int length, int height, boolean enabled, @NotNull Plugin plugin) {
+ this(length, height, Priority.NORMAL, enabled, plugin);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param length the length
+ * @param height the height
+ * @param plugin the plugin that will be the owner of this button's items
+ * @since 0.12.0
+ */
+ public ToggleButton(int length, int height, @NotNull Plugin plugin) {
+ this(length, height, false, plugin);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param length the length
+ * @param height the height
+ * @param priority the priority
+ * @param enabled whether the button should start in its enabled or disabled state
+ * @since 0.12.0
+ */
+ public ToggleButton(int length, int height, @NotNull Priority priority, boolean enabled) {
+ this(length, height, priority, enabled, JavaPlugin.getProvidingPlugin(ToggleButton.class));
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param length the length
+ * @param height the height
+ * @param priority the priority
+ * @since 0.12.0
+ */
+ public ToggleButton(int length, int height, @NotNull Priority priority) {
+ this(length, height, priority, false);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param length the length
+ * @param height the height
+ * @param enabled whether the button should start in its enabled or disabled state
+ * @since 0.12.0
+ */
+ public ToggleButton(int length, int height, boolean enabled) {
+ this(length, height, Priority.NORMAL, enabled);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param length the length
+ * @param height the height
+ * @since 0.12.0
+ */
+ public ToggleButton(int length, int height) {
+ this(length, height, false);
+ }
+
+ @NotNull
+ @Override
+ public GuiItemContainer display() {
+ if (enabled) {
+ return this.enabledPane.display();
+ } else {
+ return this.disabledPane.display();
+ }
+ }
+
+ @Override
+ public boolean click(@NotNull Gui gui, @NotNull GuiComponent guiComponent, @NotNull InventoryClickEvent event,
+ @NotNull Slot slot) {
+ int x = slot.getX(getLength());
+ int y = slot.getY(getLength());
+
+ //this isn't our item
+ if (x < 0 || x >= getLength() || y < 0 || y >= getHeight()) {
+ return false;
+ }
+
+ if (this.allowToggle) {
+ toggle();
+ }
+
+ callOnClick(event);
+
+ /*
+ Since we've toggled before, the click for the panes should be swapped around. If we haven't toggled due to
+ allowToggle being false, then we should click the pane corresponding to the current state. An XOR achieves this.
+ */
+ if (enabled == this.allowToggle) {
+ this.disabledPane.click(gui, guiComponent, event, slot);
+ } else {
+ this.enabledPane.click(gui, guiComponent, event, slot);
+ }
+
+ event.setCancelled(true);
+
+ gui.update();
+
+ return true;
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public ToggleButton copy() {
+ ToggleButton toggleButton = new ToggleButton(getLength(), getHeight(), getPriority(), isEnabled());
+
+ toggleButton.allowToggle = this.allowToggle;
+
+ toggleButton.setVisible(isVisible());
+ toggleButton.onClick = onClick;
+
+ toggleButton.uuid = uuid;
+
+ toggleButton.setEnabledItem(enabledPane.getItems().get(0).copy());
+ toggleButton.setDisabledItem(disabledPane.getItems().get(0).copy());
+
+ return toggleButton;
+ }
+
+ @Override
+ public void setLength(int length) {
+ super.setLength(length);
+
+ this.disabledPane.setLength(length);
+ this.enabledPane.setLength(length);
+ }
+
+ @Override
+ public void setHeight(int height) {
+ super.setHeight(height);
+
+ this.disabledPane.setHeight(height);
+ this.enabledPane.setHeight(height);
+ }
+
+ /**
+ * Sets the item to use when the button is set to disabled
+ *
+ * @param item the disabled item
+ * @since 0.5.0
+ */
+ public void setDisabledItem(@NotNull GuiItem item) {
+ disabledPane.clear();
+
+ disabledPane.addItem(item);
+ }
+
+ /**
+ * Sets the item to use when the button is set to enabled
+ *
+ * @param item the enabled item
+ * @since 0.5.0
+ */
+ public void setEnabledItem(@NotNull GuiItem item) {
+ enabledPane.clear();
+
+ enabledPane.addItem(item);
+ }
+
+ @NotNull
+ @Override
+ public Collection getItems() {
+ return getPanes().stream().flatMap(pane -> pane.getItems().stream()).collect(Collectors.toSet());
+ }
+
+ @NotNull
+ @Override
+ public Collection getPanes() {
+ return Stream.of(enabledPane, disabledPane).collect(Collectors.toSet());
+ }
+
+ /**
+ * Sets whether this toggle button can be toggled. This only prevents players from toggling the button and does not
+ * prevent toggling the button programmatically with methods such as {@link #toggle()}.
+ *
+ * @param allowToggle whether this button can be toggled
+ * @since 0.10.8
+ */
+ public void allowToggle(boolean allowToggle) {
+ this.allowToggle = allowToggle;
+ }
+
+ /**
+ * Gets whether this toggle button is currently enabled or disabled.
+ *
+ * @return whether the button is enabled or disabled
+ * @since 0.9.6
+ */
+ @Contract(pure = true)
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Toggles between the enabled and disabled states
+ *
+ * @since 0.5.0
+ */
+ public void toggle() {
+ enabled = !enabled;
+ }
+
+ @Override
+ public void clear() {}
+
+ /**
+ * Loads a toggle button from an XML element
+ *
+ * @param instance the instance class
+ * @param element the element
+ * @param plugin the plugin that will be the owner of the underlying items
+ * @return the toggle button
+ * @since 0.10.8
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static ToggleButton load(@NotNull Object instance, @NotNull Element element, @NotNull Plugin plugin) {
+ if (!element.hasAttribute("length")) {
+ throw new XMLLoadException("Toggle button XML tag does not have the mandatory length attribute");
+ }
+
+ if (!element.hasAttribute("height")) {
+ throw new XMLLoadException("Toggle button XML tag does not have the mandatory height attribute");
+ }
+
+ int length;
+ int height;
+
+ try {
+ length = Integer.parseInt(element.getAttribute("length"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Length attribute is not an integer", exception);
+ }
+
+ try {
+ height = Integer.parseInt(element.getAttribute("height"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Height attribute is not an integer", exception);
+ }
+
+ boolean enabled = element.hasAttribute("enabled") && Boolean.parseBoolean(element.getAttribute("enabled"));
+ ToggleButton toggleButton = new ToggleButton(length, height, enabled, plugin);
+
+ Pane.load(toggleButton, instance, element);
+
+ return toggleButton;
+ }
+}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/util/VariableBar.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/util/VariableBar.java
index 1b07921fe..79f4b9ea6 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/util/VariableBar.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/util/VariableBar.java
@@ -1,13 +1,15 @@
package com.github.stefvanschie.inventoryframework.pane.component.util;
-import com.github.stefvanschie.inventoryframework.gui.InventoryComponent;
import com.github.stefvanschie.inventoryframework.gui.GuiItem;
import com.github.stefvanschie.inventoryframework.pane.Flippable;
import com.github.stefvanschie.inventoryframework.pane.Orientable;
import com.github.stefvanschie.inventoryframework.pane.OutlinePane;
import com.github.stefvanschie.inventoryframework.pane.Pane;
+import com.github.stefvanschie.inventoryframework.pane.util.GuiItemContainer;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
@@ -43,37 +45,64 @@ public abstract class VariableBar extends Pane implements Orientable, Flippable
*/
protected boolean flipHorizontally, flipVertically;
- protected VariableBar(int length, int height) {
+ /**
+ * Creates a new variable bar
+ *
+ * @param length the length of the bar
+ * @param height the height of the bar
+ * @param plugin the plugin that will be the owner for this variable bar's items
+ * @see #VariableBar(int, int)
+ * @since 0.10.8
+ */
+ protected VariableBar(int length, int height, @NotNull Plugin plugin) {
+ this(length, height, Priority.NORMAL, plugin);
+ }
+
+ /**
+ * Creates a new variable bar
+ *
+ * @param length the length of the bar
+ * @param height the height of the bar
+ * @param priority the priority of the bar
+ * @param plugin the plugin that will be the owner for this variable bar's items
+ * @since 0.12.0
+ */
+ protected VariableBar(int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) {
super(length, height);
this.value = 0F;
this.orientation = Orientation.HORIZONTAL;
- this.fillPane = new OutlinePane(0, 0, length, height);
- this.backgroundPane = new OutlinePane(0, 0, length, height);
+ this.fillPane = new OutlinePane(length, height);
+ this.backgroundPane = new OutlinePane(length, height);
this.fillPane.addItem(new GuiItem(new ItemStack(Material.GREEN_STAINED_GLASS_PANE),
- event -> event.setCancelled(true)));
+ event -> event.setCancelled(true), plugin));
this.backgroundPane.addItem(new GuiItem(new ItemStack(Material.RED_STAINED_GLASS_PANE),
- event -> event.setCancelled(true)));
+ event -> event.setCancelled(true), plugin));
this.fillPane.setRepeat(true);
this.backgroundPane.setRepeat(true);
this.fillPane.setVisible(false);
- }
-
- protected VariableBar(int x, int y, int length, int height, @NotNull Priority priority) {
- this(length, height);
-
- setX(x);
- setY(y);
setPriority(priority);
}
- protected VariableBar(int x, int y, int length, int height) {
- this(x, y, length, height, Priority.NORMAL);
+ protected VariableBar(int length, int height) {
+ this(length, height, JavaPlugin.getProvidingPlugin(VariableBar.class));
+ }
+
+ /**
+ * Creates a new variable bar
+ *
+ * @param length the length of the bar
+ * @param height the height of the bar
+ * @param priority the priority of the bar
+ * @since 0.12.0
+ */
+ protected VariableBar(int length, int height, @NotNull Priority priority) {
+ this(length, height, priority, JavaPlugin.getProvidingPlugin(VariableBar.class));
}
/**
@@ -100,10 +129,6 @@ protected void setValue(float value) {
if (positiveLength) {
this.fillPane.setLength(length);
}
-
- if (flipHorizontally) {
- this.fillPane.setX(getLength() - this.fillPane.getLength());
- }
} else if (orientation == Orientation.VERTICAL) {
int height = Math.round(getHeight() * value);
boolean positiveHeight = height != 0;
@@ -113,10 +138,6 @@ protected void setValue(float value) {
if (positiveHeight) {
this.fillPane.setHeight(height);
}
-
- if (flipVertically) {
- this.fillPane.setY(getHeight() - this.fillPane.getHeight());
- }
} else {
throw new UnsupportedOperationException("Unknown orientation");
}
@@ -126,23 +147,12 @@ protected void setValue(float value) {
public void setLength(int length) {
super.setLength(length);
- if (orientation == Orientation.HORIZONTAL) {
- int fillLength = Math.round(length * value);
- boolean positiveLength = fillLength != 0;
+ boolean isPositive = length != 0;
- this.fillPane.setVisible(positiveLength);
-
- if (positiveLength) {
- this.fillPane.setLength(fillLength);
- }
+ this.fillPane.setVisible(isPositive);
- if (flipHorizontally) {
- this.fillPane.setX(getLength() - this.fillPane.getLength());
- }
- } else if (orientation == Orientation.VERTICAL) {
+ if (isPositive) {
this.fillPane.setLength(length);
- } else {
- throw new UnsupportedOperationException("Unknown orientation");
}
this.backgroundPane.setLength(length);
@@ -152,23 +162,12 @@ public void setLength(int length) {
public void setHeight(int height) {
super.setHeight(height);
- if (orientation == Orientation.HORIZONTAL) {
- this.fillPane.setHeight(height);
- } else if (orientation == Orientation.VERTICAL) {
- int fillHeight = Math.round(height * value);
- boolean positiveHeight = fillHeight != 0;
-
- this.fillPane.setVisible(positiveHeight);
+ boolean isPositive = height != 0;
- if (positiveHeight) {
- this.fillPane.setHeight(fillHeight);
- }
+ this.fillPane.setVisible(isPositive);
- if (flipVertically) {
- this.fillPane.setY(getHeight() - this.fillPane.getHeight());
- }
- } else {
- throw new UnsupportedOperationException("Unknown orientation");
+ if (isPositive) {
+ this.fillPane.setHeight(height);
}
this.backgroundPane.setHeight(height);
@@ -182,8 +181,6 @@ public void setHeight(int height) {
* @since 0.6.2
*/
protected void applyContents(@NotNull VariableBar copy) {
- copy.x = x;
- copy.y = y;
copy.length = length;
copy.height = height;
copy.setPriority(getPriority());
@@ -207,42 +204,24 @@ protected void applyContents(@NotNull VariableBar copy) {
public void setOrientation(@NotNull Orientation orientation) {
this.orientation = orientation;
- if (orientation == Orientation.HORIZONTAL) {
- int fillLength = Math.round(getLength() * value);
- boolean positiveLength = fillLength != 0;
-
- fillPane.setVisible(fillLength != 0);
-
- if (positiveLength) {
- fillPane.setLength(fillLength);
- }
+ this.fillPane.setOrientation(orientation);
+ this.backgroundPane.setOrientation(orientation);
+ }
- fillPane.setHeight(getHeight());
- } else if (orientation == Orientation.VERTICAL) {
- int fillHeight = Math.round(getHeight() * value);
- boolean positiveHeight = fillHeight != 0;
+ @NotNull
+ @Override
+ public GuiItemContainer display() {
+ GuiItemContainer container = new GuiItemContainer(getLength(), getHeight());
- fillPane.setVisible(fillHeight != 0);
- fillPane.setLength(getLength());
+ if (this.backgroundPane.isVisible()) {
+ container.apply(this.backgroundPane.display(), 0, 0);
+ }
- if (positiveHeight) {
- fillPane.setHeight(fillHeight);
- }
- } else {
- throw new IllegalArgumentException("Unknown orientation");
+ if (this.fillPane.isVisible()) {
+ container.apply(this.fillPane.display(), 0, 0);
}
- }
- @Override
- public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength,
- int maxHeight) {
- int newPaneOffsetX = paneOffsetX + getX();
- int newPaneOffsetY = paneOffsetY + getY();
- int newMaxLength = Math.min(maxLength, getLength());
- int newMaxHeight = Math.min(maxHeight, getHeight());
-
- this.backgroundPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, newMaxLength, newMaxHeight);
- this.fillPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, newMaxLength, newMaxHeight);
+ return container;
}
/**
@@ -284,11 +263,17 @@ public Collection getPanes() {
@Override
public void flipHorizontally(boolean flipHorizontally) {
this.flipHorizontally = flipHorizontally;
+
+ this.fillPane.flipHorizontally(flipHorizontally);
+ this.backgroundPane.flipHorizontally(flipHorizontally);
}
@Override
public void flipVertically(boolean flipVertically) {
this.flipVertically = flipVertically;
+
+ this.fillPane.flipVertically(flipVertically);
+ this.backgroundPane.flipVertically(flipVertically);
}
@NotNull
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/GuiItemContainer.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/GuiItemContainer.java
new file mode 100644
index 000000000..6b5e3b891
--- /dev/null
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/GuiItemContainer.java
@@ -0,0 +1,290 @@
+package com.github.stefvanschie.inventoryframework.pane.util;
+
+import com.github.stefvanschie.inventoryframework.gui.GuiItem;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.Range;
+
+import java.util.Arrays;
+
+/**
+ * A container for storing a grid of {@link GuiItem}s.
+ *
+ * @since 0.12.0
+ */
+public class GuiItemContainer {
+
+ /**
+ * The items stored in this grid, stored in column-major order. Slots that are empty are represented as
+ * {@literal null}.
+ */
+ @Nullable
+ private final GuiItem @NotNull [] @NotNull [] items;
+ /**
+ * The length and height of this container.
+ */
+ @Range(from = 0, to = Integer.MAX_VALUE)
+ private final int length, height;
+
+ /**
+ * Creates a new container with the provided length and height. The length and height must both be positive numbers.
+ *
+ * @param length the length of the container
+ * @param height the height of the container
+ * @since 0.12.0
+ */
+ public GuiItemContainer(@Range(from = 0, to = Integer.MAX_VALUE) int length,
+ @Range(from = 0, to = Integer.MAX_VALUE) int height) {
+ this.length = length;
+ this.height = height;
+ this.items = new GuiItem[length][height];
+ }
+
+ /**
+ * Returns a new container, excluding the range of specified rows. The new container will have its size shrunk so
+ * only the included rows are present and any items in the excluded rows are discarded. Note that while this does
+ * make a new container, it does not make a copy. For example, the items in the new gui component will be the exact
+ * same items as in this one and will not be copied. The specified range is 0-indexed: the first row starts at index
+ * 0 and the last row ends at height - 1. The range is inclusive on both ends, the row specified at either parameter
+ * will also be excluded. When the range specified is invalid - that is, part of the range contains rows that are
+ * not included in this container, an {@link IllegalArgumentException} will be thrown.
+ *
+ * @param from the starting index of the range
+ * @param end the ending index of the range
+ * @return the new, shrunk container
+ * @since 0.12.0
+ */
+ @NotNull
+ @Contract(value = "_, _ -> new", pure = true)
+ public GuiItemContainer excludeRows(@Range(from = 0, to = Integer.MAX_VALUE) int from, int end) {
+ if (end >= getHeight()) {
+ throw new IllegalArgumentException("Specified range includes non-existent rows");
+ }
+
+ GuiItemContainer newGuiContainer = new GuiItemContainer(getLength(), getHeight() - (end - from + 1));
+
+ for (int x = 0; x < getLength(); x++) {
+ int newY = 0;
+
+ for (int y = 0; y < getHeight(); y++) {
+ GuiItem item = getItem(x, y);
+
+ if (y >= from && y <= end) {
+ continue;
+ }
+
+ newGuiContainer.items[x][newY] = item;
+ newY++;
+ }
+ }
+
+ return newGuiContainer;
+ }
+
+ /**
+ * Creates a deep copy of this container. This means that all internal items will be cloned. The returned container
+ * is guaranteed to not reference equals this container.
+ *
+ * @return the new container
+ * @since 0.12.0
+ */
+ @NotNull
+ @Contract(value = "-> new", pure = true)
+ public GuiItemContainer copy() {
+ GuiItemContainer copy = new GuiItemContainer(getLength(), getHeight());
+
+ for (int x = 0; x < getLength(); x++) {
+ for (int y = 0; y < getHeight(); y++) {
+ GuiItem item = getItem(x, y);
+
+ if (item == null) {
+ continue;
+ }
+
+ copy.items[x][y] = item.copy();
+ }
+ }
+
+ return copy;
+ }
+
+ /**
+ * Checks whether this container has at least one item. If it does, true is returned; false otherwise.
+ *
+ * @return {@literal true} if this has an item, {@literal false} otherwise
+ * @since 0.12.0
+ */
+ @Contract(pure = true)
+ public boolean hasItem() {
+ for (int x = 0; x < getLength(); x++) {
+ for (int y = 0; y < getHeight(); y++) {
+ if (hasItem(x, y)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds the specified item in the slot at the specified positions. This will override an already set item if it
+ * resides in the same position as specified. If the position specified is outside the boundaries set by this
+ * container, an {@link IllegalArgumentException} will be thrown.
+ *
+ * @param guiItem the item to place in this container
+ * @param x the x coordinate of the item
+ * @param y the y coordinate of the item
+ * @since 0.12.0
+ * @throws IllegalArgumentException when the coordinates are out of bounds
+ */
+ public void setItem(@NotNull GuiItem guiItem, @Range(from = 0, to = Integer.MAX_VALUE) int x,
+ @Range(from = 0, to = Integer.MAX_VALUE) int y) {
+ if (!isInBounds(x, y)) {
+ throw new IllegalArgumentException("Coordinates must be in-bounds: x = " + x + ", y = " + y +
+ "; should be below " + getLength() + " and " + getHeight());
+ }
+
+ GuiItem copy = guiItem.copy();
+ copy.applyUUID();
+
+ this.items[x][y] = copy;
+ }
+
+ /**
+ * Gets the item at the specified coordinates, or null if this cell is empty. If the specified coordinates are not
+ * within this container, an {@link IllegalArgumentException} will be thrown.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @return the item or null
+ * @since 0.12.0
+ * @throws IllegalArgumentException when the coordinates are out of bounds
+ */
+ @Nullable
+ @Contract(pure = true)
+ public GuiItem getItem(@Range(from = 0, to = Integer.MAX_VALUE) int x,
+ @Range(from = 0, to = Integer.MAX_VALUE) int y) {
+ if (!isInBounds(x, y)) {
+ throw new IllegalArgumentException("Coordinates must be in-bounds: x = " + x + ", y = " + y +
+ "; should be below " + getLength() + " and " + getHeight());
+ }
+
+ return this.items[x][y];
+ }
+
+ /**
+ * Clears the items of this container.
+ *
+ * @since 0.12.0
+ */
+ public void clearItems() {
+ for (GuiItem[] items : this.items) {
+ Arrays.fill(items, null);
+ }
+ }
+
+ /**
+ * Checks whether the item at the specified coordinates exists. If the specified coordinates are not within this
+ * container, an {@link IllegalArgumentException} will be thrown.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @return {@literal true} if an item exists at the given coordinates, {@literal false} otherwise
+ * @since 0.12.0
+ * @throws IllegalArgumentException when the coordinates are out of bounds
+ */
+ @Contract(pure = true)
+ public boolean hasItem(@Range(from = 0, to = Integer.MAX_VALUE) int x,
+ @Range(from = 0, to = Integer.MAX_VALUE) int y) {
+ return getItem(x, y) != null;
+ }
+
+ /**
+ * Gets the height of this container.
+ *
+ * @return the height
+ * @since 0.12.0
+ */
+ @Contract(pure = true)
+ @Range(from = 0, to = Integer.MAX_VALUE)
+ public int getHeight() {
+ return this.height;
+ }
+
+ /**
+ * Gets the length of this container.
+ *
+ * @return the length
+ * @since 0.12.0
+ */
+ @Contract(pure = true)
+ @Range(from = 0, to = Integer.MAX_VALUE)
+ public int getLength() {
+ return this.length;
+ }
+
+ /**
+ * Puts all items from the container in this container. The items will not be copied, nor will their UUID be applied
+ * again. The items will be placed starting at the specified x and y coordinates. Any items that are outside the
+ * confines of this container will be ignored. The provided container will not be modified. Any items that were
+ * already in this container will be overwritten by the items from the provided container. However, currently
+ * existing items will not be overwritten with {@literal null}.
+ *
+ * @param container the container to obtain items from
+ * @param startX the starting x coordinate
+ * @param startY the starting y coordinate
+ * @since 0.12.0
+ */
+ public void apply(@NotNull GuiItemContainer container, @Range(from = 0, to = Integer.MAX_VALUE) int startX,
+ @Range(from = 0, to = Integer.MAX_VALUE) int startY) {
+ for (int x = 0; x < container.getLength(); x++) {
+ for (int y = 0; y < container.getHeight(); y++) {
+ if (!isInBounds(x + startX, y + startY)) {
+ continue;
+ }
+
+ GuiItem item = container.getItem(x, y);
+
+ if (item == null) {
+ continue;
+ }
+
+ this.items[x + startX][y + startY] = item;
+ }
+ }
+ }
+
+ /**
+ * Returns whether the specified coordinates are inside the boundary of this container. {@literal true} is returned
+ * if they are and {@literal false} otherwise.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @return {@literal true} if the coordinates are in bounds, {@literal false} otherwise
+ * @since 0.12.0
+ */
+ @Contract(pure = true)
+ private boolean isInBounds(@Range(from = 0, to = Integer.MAX_VALUE) int x,
+ @Range(from = 0, to = Integer.MAX_VALUE) int y) {
+ boolean xBounds = isInBounds(0, getLength() - 1, x);
+ boolean yBounds = isInBounds(0, getHeight() - 1, y);
+
+ return xBounds && yBounds;
+ }
+
+ /**
+ * Checks whether a number is within the specified number bound (inclusive on both ends).
+ *
+ * @param lowerBound the lower bound of the range
+ * @param upperBound the upper bound of the range
+ * @param value the value to check
+ * @return {@literal true} if the value is within the bounds, {@literal false} otherwise
+ * @since 0.12.0
+ */
+ @Contract(pure = true)
+ private boolean isInBounds(int lowerBound, int upperBound, int value) {
+ return lowerBound <= value && value <= upperBound;
+ }
+}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Mask.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Mask.java
index 55bce832f..901d854a4 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Mask.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Mask.java
@@ -154,7 +154,7 @@ public int amountOfEnabledSlots() {
* @since 0.5.16
*/
public boolean[] getColumn(int index) {
- boolean[] column = new boolean[mask[0].length];
+ boolean[] column = new boolean[mask.length];
for (int i = 0; i < getHeight(); i++) {
column[i] = mask[i][index];
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/PositionedPane.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/PositionedPane.java
new file mode 100644
index 000000000..f2498a803
--- /dev/null
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/PositionedPane.java
@@ -0,0 +1,49 @@
+package com.github.stefvanschie.inventoryframework.pane.util;
+
+import com.github.stefvanschie.inventoryframework.pane.Pane;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * An object for representing a pane with its slot.
+ *
+ * @since 0.12.0
+ */
+public class PositionedPane {
+
+ /**
+ * The slot of the pane.
+ */
+ @NotNull
+ private final Slot slot;
+
+ /**
+ * The pane.
+ */
+ @NotNull
+ private final Pane pane;
+
+ /**
+ * Creates a new positioned pane at the provided position.
+ *
+ * @param slot the slot of the pane
+ * @param pane the pane
+ * @since 0.12.0
+ */
+ public PositionedPane(@NotNull Slot slot, @NotNull Pane pane) {
+ this.slot = slot;
+ this.pane = pane;
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ public Slot getSlot() {
+ return this.slot;
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ public Pane getPane() {
+ return this.pane;
+ }
+}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Slot.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Slot.java
new file mode 100644
index 000000000..7f1037ded
--- /dev/null
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/util/Slot.java
@@ -0,0 +1,254 @@
+package com.github.stefvanschie.inventoryframework.pane.util;
+
+import com.github.stefvanschie.inventoryframework.exception.XMLLoadException;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.w3c.dom.Element;
+
+import java.util.Objects;
+
+/**
+ * A slot represents a position in some type of container. Implementors of this class represent slots in different ways.
+ *
+ * @since 0.10.8
+ */
+public interface Slot {
+
+ /**
+ * Gets the x coordinate of this slot.
+ *
+ * @param length the length of the parent container
+ * @return the x coordinate of this slot
+ * @since 0.10.8
+ */
+ @Contract(pure = true)
+ int getX(int length);
+
+ /**
+ * Gets the y coordinate of this slot.
+ *
+ * @param length the length of the parent container
+ * @return the y coordinate of this slot
+ * @since 0.10.8
+ */
+ @Contract(pure = true)
+ int getY(int length);
+
+ /**
+ * Deserializes the slot from an element. The slot may either be provided as an (x, y) coordinate pair via the "x"
+ * and "y" attributes; or as an index via the "index" attribute. If both forms are present, an
+ * {@link XMLLoadException} will be thrown. If only the "x" or the "y" attribute is present, but not both, an
+ * {@link XMLLoadException} will be thrown. If none of the aforementioned attributes appear, an
+ * {@link XMLLoadException} will be thrown. If any of these attributes contain a value that is not an integer, an
+ * {@link XMLLoadException} will be thrown. Otherwise, this will return a slot based on the present attributes.
+ *
+ * @param element the element from which to retrieve the attributes for the slot
+ * @return the deserialized slot
+ * @throws XMLLoadException if "x", "y", and "index" attributes are present; if only an "x" attribute is present; if
+ * only a "y" attribute is present; if no "x", "y", or "index" attribute is present; or if
+ * the "x", "y", or "index" attribute contain a value that is not an integer.
+ */
+ @NotNull
+ @Contract(value = "_ -> new", pure = true)
+ static Slot deserialize(@NotNull Element element) {
+ boolean hasX = element.hasAttribute("x");
+ boolean hasY = element.hasAttribute("y");
+ boolean hasIndex = element.hasAttribute("index");
+
+ if (hasX && hasY && !hasIndex) {
+ int x, y;
+
+ try {
+ x = Integer.parseInt(element.getAttribute("x"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("The x attribute does not have an integer as value", exception);
+ }
+
+ try {
+ y = Integer.parseInt(element.getAttribute("y"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("The y attribute does not have an integer as value", exception);
+ }
+
+ return Slot.fromXY(x, y);
+ }
+
+ if (hasIndex && !hasX && !hasY) {
+ int index;
+
+ try {
+ index = Integer.parseInt(element.getAttribute("index"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("The index attribute does not have an integer as value", exception);
+ }
+
+ return Slot.fromIndex(index);
+ }
+
+ throw new XMLLoadException("The combination of x, y and index attributes is invalid");
+ }
+
+ /**
+ * Creates a new slot based on an (x, y) coordinate pair.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @return the slot representing this position
+ * @since 0.10.8
+ */
+ @NotNull
+ @Contract(value = "_, _ -> new", pure = true)
+ static Slot fromXY(int x, int y) {
+ return new XY(x, y);
+ }
+
+ /**
+ * Creates a new slot based on an index. This index is relative to the parent container this slot will be used in.
+ *
+ * @param index the index
+ * @return the slot representing this relative position
+ * @since 0.10.8
+ */
+ @NotNull
+ @Contract("_ -> new")
+ static Slot fromIndex(int index) {
+ return new Indexed(index);
+ }
+
+ /**
+ * A class representing a slot based on an (x, y) coordinate pair.
+ *
+ * @since 0.10.8
+ */
+ class XY implements Slot {
+
+ /**
+ * The (x, y) coordinate pair
+ */
+ private final int x, y;
+
+ /**
+ * Creates a new slot based on an (x, y) coordinate pair.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @since 0.10.8
+ */
+ private XY(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ @Override
+ public int getX(int length) {
+ return this.x;
+ }
+
+ @Override
+ public int getY(int length) {
+ return this.y;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (this == object) {
+ return true;
+ }
+
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+
+ XY xy = (XY) object;
+
+ return x == xy.x && y == xy.y;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(x, y);
+ }
+ }
+
+ /**
+ * A class representing a slot based on an index.
+ *
+ * @since 0.10.8
+ */
+ class Indexed implements Slot {
+
+ /**
+ * The index of this slot.
+ */
+ private final int index;
+
+ /**
+ * Creates a new slot based on an index.
+ *
+ * @param index the index of this slot
+ * @since 0.10.8
+ */
+ private Indexed(int index) {
+ this.index = index;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * If {@code length} is zero, this will throw an {@link IllegalArgumentException}.
+ *
+ * @param length {@inheritDoc}
+ * @return {@inheritDoc}
+ * @throws IllegalArgumentException when {@code length} is zero
+ */
+ @Override
+ @Contract(pure = true)
+ public int getX(int length) {
+ if (length == 0) {
+ throw new IllegalArgumentException("Length may not be zero");
+ }
+
+ return this.index % length;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * If {@code length} is zero, this will throw an {@link IllegalArgumentException}.
+ *
+ * @param length {@inheritDoc}
+ * @return {@inheritDoc}
+ * @throws IllegalArgumentException when {@code length} is zero
+ */
+ @Override
+ @Contract(pure = true)
+ public int getY(int length) {
+ if (length == 0) {
+ throw new IllegalArgumentException("Length may not be zero");
+ }
+
+ return this.index / length;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (this == object) {
+ return true;
+ }
+
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+
+ Indexed indexed = (Indexed) object;
+
+ return index == indexed.index;
+ }
+
+ @Override
+ public int hashCode() {
+ return index;
+ }
+ }
+}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/CSVUtil.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/CSVUtil.java
index 1f2b75797..89d05da5f 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/CSVUtil.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/CSVUtil.java
@@ -1,6 +1,5 @@
package com.github.stefvanschie.inventoryframework.util;
-import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedReader;
@@ -72,9 +71,7 @@ public static List readAll(@NotNull InputStream inputStream) throws IO
array[i] = array[i].substring(1, array[i].length() - 1);
}
- array[i] = StringUtils.replace(array[i], "\"\"", "\"");
- //Restore original code (array[i] = array[i].replace("\"\"", "\""))
- //once we update to Java 11, where it receives the current, faster implementation
+ array[i] = array[i].replace("\"\"", "\"");
//replace unicode characters
Matcher matcher = UNICODE_CHARACTER_PATTERN.matcher(array[i]);
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/InventoryViewUtil.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/InventoryViewUtil.java
new file mode 100644
index 000000000..8d6327e69
--- /dev/null
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/InventoryViewUtil.java
@@ -0,0 +1,42 @@
+package com.github.stefvanschie.inventoryframework.util;
+
+import com.github.stefvanschie.inventoryframework.inventoryview.abstraction.AbstractInventoryViewUtil;
+import com.github.stefvanschie.inventoryframework.util.version.Version;
+import org.bukkit.inventory.InventoryView;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A utility class for working with {@link InventoryView}s across different definitions.
+ *
+ * @since 0.10.16
+ */
+public class InventoryViewUtil {
+
+ /**
+ * The underlying implementation.
+ */
+ @Nullable
+ private static AbstractInventoryViewUtil IMPLEMENTATION;
+
+ /**
+ * Gets the instance of this class to use for the current version.
+ *
+ * @return an instance of a utility class
+ * @since 0.10.16
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static AbstractInventoryViewUtil getInstance() {
+ if (IMPLEMENTATION == null) {
+ if (Version.getVersion().isInventoryViewInterface()) {
+ IMPLEMENTATION = com.github.stefvanschie.inventoryframework.inventoryview.interface_.InventoryViewUtil.getInstance();
+ } else {
+ IMPLEMENTATION = com.github.stefvanschie.inventoryframework.inventoryview.abstractclass.InventoryViewUtil.getInstance();
+ }
+ }
+
+ return IMPLEMENTATION;
+ }
+}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/SkullUtil.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/SkullUtil.java
index b28b82bc4..befe08bc1 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/SkullUtil.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/SkullUtil.java
@@ -1,13 +1,22 @@
package com.github.stefvanschie.inventoryframework.util;
+import com.github.stefvanschie.inventoryframework.util.version.Version;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
+import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.inventory.meta.SkullMeta;
+import org.bukkit.profile.PlayerProfile;
+import org.bukkit.profile.PlayerTextures;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.util.Base64;
import java.util.Objects;
import java.util.UUID;
@@ -51,17 +60,68 @@ public static ItemStack getSkull(@NotNull String id) {
* @param id the skull id
*/
public static void setSkull(@NotNull ItemMeta meta, @NotNull String id) {
- GameProfile profile = new GameProfile(UUID.randomUUID(), null);
+ if (Version.getVersion().isOlderThan(Version.V1_18_2)) {
+ setSkullReflection(meta, id);
+ } else {
+ setSkullProfile(meta, id);
+ }
+ }
+
+ /**
+ * Sets the skull's texture based on the player profile API introduced in 1.18.2.
+ *
+ * @param meta the {@link ItemMeta} of the skull to set
+ * @param id the ID of the skin URL to apply
+ * @since 0.11.6
+ */
+ private static void setSkullProfile(@NotNull ItemMeta meta, @NotNull String id) {
+ if (!(meta instanceof SkullMeta)) {
+ throw new IllegalArgumentException("Provided item meta is not of a skull");
+ }
+
+ PlayerProfile profile = Bukkit.createPlayerProfile(UUID.randomUUID());
+ PlayerTextures textures = profile.getTextures();
+
+ try {
+ textures.setSkin(new URL("http://textures.minecraft.net/texture/" + id));
+ } catch (MalformedURLException exception) {
+ throw new IllegalArgumentException("Provided ID is invalid", exception);
+ }
+
+ profile.setTextures(textures);
+
+ ((SkullMeta) meta).setOwnerProfile(profile);
+ }
+
+ /**
+ * Sets the skull's texture via reflection.
+ *
+ * @param meta the {@link ItemMeta} of the skull to set
+ * @param id the ID of the skin URL to apply
+ * @since 0.11.6
+ */
+ private static void setSkullReflection(@NotNull ItemMeta meta, @NotNull String id) {
+ GameProfile profile = new GameProfile(UUID.randomUUID(), "");
byte[] encodedData = Base64.getEncoder().encode(String.format("{textures:{SKIN:{url:\"%s\"}}}",
- "http://textures.minecraft.net/texture/" + id).getBytes());
+ "http://textures.minecraft.net/texture/" + id).getBytes());
profile.getProperties().put("textures", new Property("textures", new String(encodedData)));
+ String itemDisplayName = meta.getDisplayName();
try {
Field profileField = meta.getClass().getDeclaredField("profile");
profileField.setAccessible(true);
profileField.set(meta, profile);
- } catch (NoSuchFieldException | SecurityException | IllegalAccessException e) {
+
+ meta.setDisplayName(itemDisplayName);
+
+ // Sets serializedProfile field on meta
+ // If it does throw NoSuchMethodException this stops, and meta is correct.
+ // Else it has profile and will set the field.
+ Method setProfile = meta.getClass().getDeclaredMethod("setProfile", GameProfile.class);
+ setProfile.setAccessible(true);
+ setProfile.invoke(meta, profile);
+ } catch (NoSuchFieldException | SecurityException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
- }
+ } catch (NoSuchMethodException ignored) {}
}
}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/TriFunction.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/TriFunction.java
new file mode 100644
index 000000000..7ce718ca8
--- /dev/null
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/TriFunction.java
@@ -0,0 +1,25 @@
+package com.github.stefvanschie.inventoryframework.util;
+
+/**
+ * A function that takes three arguments and returns a result.
+ *
+ * @param the type of the first argument
+ * @param the type of the second argument
+ * @param the type of the third argument
+ * @param the type of the result
+ * @since 0.10.8
+ */
+@FunctionalInterface
+public interface TriFunction {
+
+ /**
+ * Applies this function to the given arguments.
+ *
+ * @param a the first argument
+ * @param b the second argument
+ * @param c the third argument
+ * @return the result value
+ * @since 0.10.8
+ */
+ R apply(A a, B b, C c);
+}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/XMLUtil.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/XMLUtil.java
index ede880dbc..1f3586bc9 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/XMLUtil.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/XMLUtil.java
@@ -62,6 +62,30 @@ public static Consumer loadOnEventAttribute(@NotNull Object
return null;
}
+ /**
+ * Invokes the method by the given name on the given instance with the provided argument. The method should have
+ * the exact name specified and the exact parameter as specified. If the method cannot be accessed or found, this
+ * will throw an {@link XMLLoadException}.
+ *
+ * @param instance the instance on which to call the method
+ * @param methodName the name of the method to invoke
+ * @param argument the argument to provide for the invocation
+ * @param parameter the parameter of the method
+ * @since 0.10.3
+ * @throws XMLLoadException if the method cannot be accessed or found
+ */
+ public static void invokeMethod(@NotNull Object instance, @NotNull String methodName, @NotNull Object argument,
+ @NotNull Class> parameter) {
+ try {
+ Method method = instance.getClass().getMethod(methodName, parameter);
+
+ method.setAccessible(true);
+ method.invoke(instance, argument);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException exception) {
+ throw new XMLLoadException(exception);
+ }
+ }
+
/**
* Sets a field from the given instance and element to the specified value
*
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/Version.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/Version.java
index aa2a2edca..c5a7e836d 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/Version.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/Version.java
@@ -2,9 +2,13 @@
import com.github.stefvanschie.inventoryframework.exception.UnsupportedVersionException;
import org.bukkit.Bukkit;
+import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
+import java.util.Collection;
+import java.util.EnumSet;
+
/**
* The different supported NMS versions
*
@@ -13,39 +17,207 @@
public enum Version {
/**
- * Version 1.14 R1
+ * Version 1.16.5
*
- * @since 0.8.0
+ * @since 0.12.0
*/
- V1_14_R1,
+ V1_16_5,
/**
- * Version 1.15 R1
+ * Version 1.17.1
*
- * @since 0.8.0
+ * @since 0.10.0
*/
- V1_15_R1,
+ V1_17_1,
/**
- * Version 1.16 R1
+ * Version 1.18.2
*
- * @since 0.8.0
+ * @since 0.10.5
*/
- V1_16_R1,
+ V1_18_2,
/**
- * Version 1.16 R2
+ * Version 1.19.4
*
- * @since 0.8.0
+ * @since 0.10.9
*/
- V1_16_R2,
+ V1_19_4,
/**
- * Version 1.16 R3
+ * Version 1.20.0
*
- * @since 0.8.0
+ * @since 0.10.14
+ */
+ V1_20_0,
+
+ /**
+ * Version 1.20.1
+ *
+ * @since 0.10.14
+ */
+ V1_20_1,
+
+ /**
+ * Version 1.20.2
+ *
+ * @since 0.10.12
+ */
+ V1_20_2,
+
+ /**
+ * Version 1.20.3 - 1.20.4
+ *
+ * @since 0.10.13
+ */
+ V1_20_3_4,
+
+ /**
+ * Version 1.20.5
+ *
+ * @since 0.10.14
+ */
+ V1_20_5,
+
+ /**
+ * Version 1.20.6
+ *
+ * @since 0.10.14
+ */
+ V1_20_6,
+
+ /**
+ * Version 1.21.0
+ *
+ * @since 0.10.18
+ */
+ V1_21_0,
+
+ /**
+ * Version 1.21.1
+ *
+ * @since 0.10.18
+ */
+ V1_21_1,
+
+ /**
+ * Version 1.21.2 - 1.21.3
+ *
+ * @since 0.10.18
+ */
+ V1_21_2_3,
+
+ /**
+ * Version 1.21.4
+ *
+ * @since 0.10.19
+ */
+ V1_21_4,
+
+ /**
+ * Version 1.21.5
+ *
+ * @since 0.11.0
+ */
+ V1_21_5,
+
+ /**
+ * Version 1.21.6 - 1.21.8
+ *
+ * @since 0.11.3
+ */
+ V1_21_6_8,
+
+ /**
+ * Version 1.21.9 - 1.21.10
+ *
+ * @since 0.11.5
+ */
+ V1_21_9_10,
+
+ /**
+ * Version 1.21.11
+ *
+ * @since 0.11.6
+ */
+ V1_21_11,
+
+ /**
+ * Version 26.1 or higher.
+ *
+ * @since 0.12.0
*/
- V1_16_R3;
+ V26_1;
+
+ /**
+ * A collection of versions on which modern smithing tables are available.
+ */
+ private static final Collection MODERN_SMITHING_TABLE_VERSIONS = EnumSet.of(
+ V1_19_4,
+ V1_20_0, V1_20_1, V1_20_2, V1_20_3_4, V1_20_5, V1_20_6,
+ V1_21_0, V1_21_1, V1_21_2_3, V1_21_4, V1_21_5, V1_21_6_8, V1_21_9_10, V1_21_11,
+ V26_1
+ );
+
+ /**
+ * A collection of versions on which legacy smithing tables ae available.
+ */
+ @NotNull
+ private static final Collection<@NotNull Version> LEGACY_SMITHING_TABLE_VERSIONS = EnumSet.of(
+ V1_16_5, V1_17_1, V1_18_2, V1_19_4
+ );
+
+ /**
+ * A collection of versions on which {@link InventoryView} is an interface.
+ */
+ @NotNull
+ private static final Collection<@NotNull Version> INTERFACE_INVENTORY_VIEW = EnumSet.of(
+ V1_21_0, V1_21_1, V1_21_2_3, V1_21_4, V1_21_5, V1_21_6_8, V1_21_9_10, V1_21_11,
+ V26_1
+ );
+
+ /**
+ * Checks whether the {@link InventoryView} class is an interface on this version.
+ *
+ * @return true if the class is an interface, false otherwise
+ * @since 0.10.16
+ */
+ @Contract(pure = true)
+ public boolean isInventoryViewInterface() {
+ return INTERFACE_INVENTORY_VIEW.contains(this);
+ }
+
+ /**
+ * Checks if this version is older than the provided version.
+ *
+ * @param version the version to check if it is newer
+ * @return true if this version is older, false otherwise
+ * @since 0.11.6
+ */
+ public boolean isOlderThan(@NotNull Version version) {
+ return ordinal() < version.ordinal();
+ }
+
+ /**
+ * Checks whether modern smithing tables exist on this version. Returns true if they do, otherwise false.
+ *
+ * @return true if modern smithing tables are available
+ * @since 0.10.10
+ */
+ boolean existsModernSmithingTable() {
+ return MODERN_SMITHING_TABLE_VERSIONS.contains(this);
+ }
+
+ /**
+ * Checks whether legacy smithing tables exist on this version. Returns true if they do, otherwise false.
+ *
+ * @return true if legacy smithing tables are available
+ * @since 0.10.10
+ */
+ @Contract(pure = true)
+ boolean existsLegacySmithingTable() {
+ return LEGACY_SMITHING_TABLE_VERSIONS.contains(this);
+ }
/**
* Gets the version currently being used. If the used version is not supported, an
@@ -57,19 +229,54 @@ public enum Version {
@NotNull
@Contract(pure = true)
public static Version getVersion() {
- String version = Bukkit.getServer().getClass().getPackage().getName();
-
- switch (version.substring(version.lastIndexOf('.') + 1)) {
- case "v1_14_R1":
- return V1_14_R1;
- case "v1_15_R1":
- return V1_15_R1;
- case "v1_16_R1":
- return V1_16_R1;
- case "v1_16_R2":
- return V1_16_R2;
- case "v1_16_R3":
- return V1_16_R3;
+ String version = Bukkit.getBukkitVersion().split("-")[0];
+
+ if (version.indexOf('.') == 2) {
+ return V26_1; //this is a 26.1+ release, so it's V26.1
+ }
+
+ switch (version) {
+ case "1.16.5":
+ return V1_16_5;
+ case "1.17.1":
+ return V1_17_1;
+ case "1.18.2":
+ return V1_18_2;
+ case "1.19.4":
+ return V1_19_4;
+ case "1.20":
+ return V1_20_0;
+ case "1.20.1":
+ return V1_20_1;
+ case "1.20.2":
+ return V1_20_2;
+ case "1.20.3":
+ case "1.20.4":
+ return V1_20_3_4;
+ case "1.20.5":
+ return V1_20_5;
+ case "1.20.6":
+ return V1_20_6;
+ case "1.21":
+ return V1_21_0;
+ case "1.21.1":
+ return V1_21_1;
+ case "1.21.2":
+ case "1.21.3":
+ return V1_21_2_3;
+ case "1.21.4":
+ return V1_21_4;
+ case "1.21.5":
+ return V1_21_5;
+ case "1.21.6":
+ case "1.21.7":
+ case "1.21.8":
+ return V1_21_6_8;
+ case "1.21.9":
+ case "1.21.10":
+ return V1_21_9_10;
+ case "1.21.11":
+ return V1_21_11;
default:
throw new UnsupportedVersionException("The server version provided is not supported");
}
diff --git a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/VersionMatcher.java b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/VersionMatcher.java
index c4f108e10..5d0f2206e 100644
--- a/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/VersionMatcher.java
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/util/version/VersionMatcher.java
@@ -2,7 +2,7 @@
import com.github.stefvanschie.inventoryframework.abstraction.*;
import com.github.stefvanschie.inventoryframework.exception.UnsupportedVersionException;
-import org.bukkit.inventory.InventoryHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_19_4.LegacySmithingTableInventoryImpl;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@@ -41,11 +41,21 @@ public class VersionMatcher {
*/
private static final EnumMap> GRINDSTONE_INVENTORIES;
+ /**
+ * The different merchant inventories for different versions
+ */
+ private static final EnumMap> MERCHANT_INVENTORIES;
+
/**
* The different smithing table inventories for different versions
*/
private static final EnumMap> SMITHING_TABLE_INVENTORIES;
+ /**
+ * The different legacy smithing table inventories for different versions
+ */
+ private static final EnumMap> LEGACY_SMITHING_TABLE_INVENTORIES;
+
/**
* The different stonecutter inventories for different versions
*/
@@ -55,15 +65,14 @@ public class VersionMatcher {
* Gets a new anvil inventory for the specified version of the specified inventory holder.
*
* @param version the version to get the inventory of
- * @param inventoryHolder the inventory holder
* @return the anvil inventory
- * @since 0.8.0
+ * @since 0.11.0
*/
@NotNull
@Contract(pure = true)
- public static AnvilInventory newAnvilInventory(@NotNull Version version, @NotNull InventoryHolder inventoryHolder) {
+ public static AnvilInventory newAnvilInventory(@NotNull Version version) {
try {
- return ANVIL_INVENTORIES.get(version).getConstructor(InventoryHolder.class).newInstance(inventoryHolder);
+ return ANVIL_INVENTORIES.get(version).getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException exception) {
throw new IllegalStateException(exception);
@@ -74,16 +83,14 @@ public static AnvilInventory newAnvilInventory(@NotNull Version version, @NotNul
* Gets a new beacon inventory for the specified version of the specified inventory holder.
*
* @param version the version to get the inventory of
- * @param inventoryHolder the inventory holder
* @return the beacon inventory
- * @since 0.8.0
+ * @since 0.11.0
*/
@NotNull
@Contract(pure = true)
- public static BeaconInventory newBeaconInventory(@NotNull Version version,
- @NotNull InventoryHolder inventoryHolder) {
+ public static BeaconInventory newBeaconInventory(@NotNull Version version) {
try {
- return BEACON_INVENTORIES.get(version).getConstructor(InventoryHolder.class).newInstance(inventoryHolder);
+ return BEACON_INVENTORIES.get(version).getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException exception) {
throw new IllegalStateException(exception);
@@ -94,18 +101,14 @@ public static BeaconInventory newBeaconInventory(@NotNull Version version,
* Gets a new cartography table inventory for the specified version of the specified inventory holder.
*
* @param version the version to get the inventory of
- * @param inventoryHolder the inventory holder
* @return the cartography table inventory
- * @since 0.8.0
+ * @since 0.11.0
*/
@NotNull
@Contract(pure = true)
- public static CartographyTableInventory newCartographyTableInventory(@NotNull Version version,
- @NotNull InventoryHolder inventoryHolder) {
+ public static CartographyTableInventory newCartographyTableInventory(@NotNull Version version) {
try {
- Class extends CartographyTableInventory> clazz = CARTOGRAPHY_TABLE_INVENTORIES.get(version);
-
- return clazz.getConstructor(InventoryHolder.class).newInstance(inventoryHolder);
+ return CARTOGRAPHY_TABLE_INVENTORIES.get(version).getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException exception) {
throw new IllegalStateException(exception);
@@ -116,18 +119,16 @@ public static CartographyTableInventory newCartographyTableInventory(@NotNull Ve
* Gets a new enchanting table inventory for the specified version of the specified inventory holder.
*
* @param version the version to get the inventory of
- * @param inventoryHolder the inventory holder
* @return the enchanting table inventory
* @since 0.8.0
*/
@NotNull
@Contract(pure = true)
- public static EnchantingTableInventory newEnchantingTableInventory(@NotNull Version version,
- @NotNull InventoryHolder inventoryHolder) {
+ public static EnchantingTableInventory newEnchantingTableInventory(@NotNull Version version) {
try {
Class extends EnchantingTableInventory> clazz = ENCHANTING_TABLE_INVENTORIES.get(version);
- return clazz.getConstructor(InventoryHolder.class).newInstance(inventoryHolder);
+ return clazz.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException exception) {
throw new IllegalStateException(exception);
@@ -138,47 +139,63 @@ public static EnchantingTableInventory newEnchantingTableInventory(@NotNull Vers
* Gets a new grindstone inventory for the specified version of the specified inventory holder.
*
* @param version the version to get the inventory of
- * @param inventoryHolder the inventory holder
* @return the grindstone inventory
* @since 0.8.0
*/
@NotNull
@Contract(pure = true)
- public static GrindstoneInventory newGrindstoneInventory(@NotNull Version version,
- @NotNull InventoryHolder inventoryHolder) {
+ public static GrindstoneInventory newGrindstoneInventory(@NotNull Version version) {
try {
Class extends GrindstoneInventory> clazz = GRINDSTONE_INVENTORIES.get(version);
- return clazz.getConstructor(InventoryHolder.class).newInstance(inventoryHolder);
+ return clazz.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException exception) {
throw new IllegalStateException(exception);
}
}
+ /**
+ * Gets a new merchant inventory for the specified version.
+ *
+ * @param version the version to get the inventory of
+ * @return the merchant inventory
+ * @since 0.10.1
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static MerchantInventory newMerchantInventory(@NotNull Version version) {
+ try {
+ return MERCHANT_INVENTORIES.get(version).getConstructor().newInstance();
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
+ NoSuchMethodException exception) {
+ throw new IllegalStateException(exception);
+ }
+ }
+
/**
* Gets a new smithing table inventory for the specified version of the specified inventory holder. If a smithing
* table is requested for a version that does not have smithing tables, an {@link UnsupportedVersionException} is
* thrown.
*
* @param version the version to get the inventory of
- * @param inventoryHolder the inventory holder
* @return the smithing table inventory
- * @since 0.8.0
+ * @since 0.10.9
* @throws UnsupportedVersionException when a smithing table is requested on a version without smithing tables
*/
@NotNull
@Contract(pure = true)
- public static SmithingTableInventory newSmithingTableInventory(@NotNull Version version,
- @NotNull InventoryHolder inventoryHolder) {
- if (version == Version.V1_14_R1 || version == Version.V1_15_R1) {
+ public static SmithingTableInventory newModernSmithingTableInventory(@NotNull Version version) {
+ if (!version.existsModernSmithingTable() && !version.existsLegacySmithingTable()) {
throw new UnsupportedVersionException("Smithing tables didn't exist in version " + version);
}
- try {
- Class extends SmithingTableInventory> clazz = SMITHING_TABLE_INVENTORIES.get(version);
+ if (!version.existsModernSmithingTable()) {
+ throw new UnsupportedVersionException("Modern smithing tables didn't exist in version " + version);
+ }
- return clazz.getConstructor(InventoryHolder.class).newInstance(inventoryHolder);
+ try {
+ return SMITHING_TABLE_INVENTORIES.get(version).getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException exception) {
throw new IllegalStateException(exception);
@@ -186,21 +203,46 @@ public static SmithingTableInventory newSmithingTableInventory(@NotNull Version
}
/**
- * Gets a new stonecutter inventory for the specified version of the specified inventory holder.
+ * Gets a new legacy smithing table inventory for the specified version of the specified inventory holder. If a
+ * smithing table is requested for a version that does not have smithing tables, an
+ * {@link UnsupportedVersionException} is thrown.
*
* @param version the version to get the inventory of
- * @param inventoryHolder the inventory holder
- * @return the stonecutter inventory
+ * @return the smithing table inventory
* @since 0.8.0
+ * @throws UnsupportedVersionException when a smithing table is requested on a version without smithing tables
*/
@NotNull
@Contract(pure = true)
- public static StonecutterInventory newStonecutterInventory(@NotNull Version version,
- @NotNull InventoryHolder inventoryHolder) {
+ public static SmithingTableInventory newSmithingTableInventory(@NotNull Version version) {
+ if (!version.existsModernSmithingTable() && !version.existsLegacySmithingTable()) {
+ throw new UnsupportedVersionException("Smithing tables didn't exist in version " + version);
+ }
+
+ if (!version.existsLegacySmithingTable()) {
+ throw new UnsupportedVersionException("Legacy smithing tables don't exist in version " + version);
+ }
+
try {
- Class extends StonecutterInventory> clazz = STONECUTTER_INVENTORIES.get(version);
+ return LEGACY_SMITHING_TABLE_INVENTORIES.get(version).getConstructor().newInstance();
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
+ NoSuchMethodException exception) {
+ throw new IllegalStateException(exception);
+ }
+ }
- return clazz.getConstructor(InventoryHolder.class).newInstance(inventoryHolder);
+ /**
+ * Gets a new stonecutter inventory for the specified version.
+ *
+ * @param version the version to get the inventory of
+ * @return the stonecutter inventory
+ * @since 0.11.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static StonecutterInventory newStonecutterInventory(@NotNull Version version) {
+ try {
+ return STONECUTTER_INVENTORIES.get(version).getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException exception) {
throw new IllegalStateException(exception);
@@ -209,83 +251,327 @@ public static StonecutterInventory newStonecutterInventory(@NotNull Version vers
static {
ANVIL_INVENTORIES = new EnumMap<>(Version.class);
- ANVIL_INVENTORIES.put(Version.V1_14_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_14_R1.AnvilInventoryImpl.class);
- ANVIL_INVENTORIES.put(Version.V1_15_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_15_R1.AnvilInventoryImpl.class);
- ANVIL_INVENTORIES.put(Version.V1_16_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R1.AnvilInventoryImpl.class);
- ANVIL_INVENTORIES.put(Version.V1_16_R2,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R2.AnvilInventoryImpl.class);
- ANVIL_INVENTORIES.put(Version.V1_16_R3,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R3.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_16_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_16_5.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_17_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_17_1.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_18_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_18_2.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_19_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_19_4.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_20_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_0.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_20_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_1.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_20_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_2.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_20_3_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_3.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_20_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_5.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_20_6,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_6.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_21_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_0.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_21_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_1.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_21_2_3,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_21_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_4.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_21_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_5.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_21_6_8,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_6_8.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_21_9_10,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_9_10.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V1_21_11,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_11.AnvilInventoryImpl.class);
+ ANVIL_INVENTORIES.put(Version.V26_1,
+ com.github.stefvanschie.inventoryframework.nms.v26_1.AnvilInventoryImpl.class);
BEACON_INVENTORIES = new EnumMap<>(Version.class);
- BEACON_INVENTORIES.put(Version.V1_14_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_14_R1.BeaconInventoryImpl.class);
- BEACON_INVENTORIES.put(Version.V1_15_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_15_R1.BeaconInventoryImpl.class);
- BEACON_INVENTORIES.put(Version.V1_16_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R1.BeaconInventoryImpl.class);
- BEACON_INVENTORIES.put(Version.V1_16_R2,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R2.BeaconInventoryImpl.class);
- BEACON_INVENTORIES.put(Version.V1_16_R3,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R3.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_16_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_16_5.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_17_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_17_1.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_18_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_18_2.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_19_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_19_4.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_20_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_0.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_20_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_1.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_20_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_2.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_20_3_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_3.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_20_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_5.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_20_6,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_6.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_21_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_0.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_21_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_1.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_21_2_3,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_21_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_4.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_21_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_5.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_21_6_8,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_6_8.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_21_9_10,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_9_10.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V1_21_11,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_11.BeaconInventoryImpl.class);
+ BEACON_INVENTORIES.put(Version.V26_1,
+ com.github.stefvanschie.inventoryframework.nms.v26_1.BeaconInventoryImpl.class);
CARTOGRAPHY_TABLE_INVENTORIES = new EnumMap<>(Version.class);
- CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_14_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_14_R1.CartographyTableInventoryImpl.class);
- CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_15_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_15_R1.CartographyTableInventoryImpl.class);
- CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_16_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R1.CartographyTableInventoryImpl.class);
- CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_16_R2,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R2.CartographyTableInventoryImpl.class);
- CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_16_R3,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R3.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_16_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_16_5.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_17_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_17_1.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_18_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_18_2.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_19_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_19_4.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_0.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_1.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_2.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_3_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_3.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_5.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_20_6,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_6.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_21_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_0.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_21_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_1.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_21_2_3,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_21_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_4.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_21_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_5.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_21_6_8,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_6_8.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_21_9_10,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_9_10.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_21_11,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_11.CartographyTableInventoryImpl.class);
+ CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V26_1,
+ com.github.stefvanschie.inventoryframework.nms.v26_1.CartographyTableInventoryImpl.class);
ENCHANTING_TABLE_INVENTORIES = new EnumMap<>(Version.class);
- ENCHANTING_TABLE_INVENTORIES.put(Version.V1_14_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_14_R1.EnchantingTableInventoryImpl.class);
- ENCHANTING_TABLE_INVENTORIES.put(Version.V1_15_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_15_R1.EnchantingTableInventoryImpl.class);
- ENCHANTING_TABLE_INVENTORIES.put(Version.V1_16_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R1.EnchantingTableInventoryImpl.class);
- ENCHANTING_TABLE_INVENTORIES.put(Version.V1_16_R2,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R2.EnchantingTableInventoryImpl.class);
- ENCHANTING_TABLE_INVENTORIES.put(Version.V1_16_R3,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R3.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_16_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_16_5.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_17_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_17_1.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_18_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_18_2.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_19_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_19_4.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_0.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_1.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_2.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_3_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_3.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_5.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_20_6,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_6.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_21_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_0.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_21_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_1.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_21_2_3,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_21_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_4.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_21_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_5.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_21_6_8,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_6_8.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_21_9_10,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_9_10.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V1_21_11,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_11.EnchantingTableInventoryImpl.class);
+ ENCHANTING_TABLE_INVENTORIES.put(Version.V26_1,
+ com.github.stefvanschie.inventoryframework.nms.v26_1.EnchantingTableInventoryImpl.class);
GRINDSTONE_INVENTORIES = new EnumMap<>(Version.class);
- GRINDSTONE_INVENTORIES.put(Version.V1_14_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_14_R1.GrindstoneInventoryImpl.class);
- GRINDSTONE_INVENTORIES.put(Version.V1_15_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_15_R1.GrindstoneInventoryImpl.class);
- GRINDSTONE_INVENTORIES.put(Version.V1_16_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R1.GrindstoneInventoryImpl.class);
- GRINDSTONE_INVENTORIES.put(Version.V1_16_R2,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R2.GrindstoneInventoryImpl.class);
- GRINDSTONE_INVENTORIES.put(Version.V1_16_R3,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R3.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_16_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_16_5.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_17_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_17_1.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_18_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_18_2.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_19_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_19_4.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_20_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_0.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_20_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_1.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_20_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_2.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_20_3_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_3.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_20_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_5.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_20_6,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_6.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_21_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_0.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_21_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_1.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_21_2_3,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_21_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_4.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_21_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_5.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_21_6_8,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_6_8.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_21_9_10,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_9_10.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V1_21_11,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_11.GrindstoneInventoryImpl.class);
+ GRINDSTONE_INVENTORIES.put(Version.V26_1,
+ com.github.stefvanschie.inventoryframework.nms.v26_1.GrindstoneInventoryImpl.class);
+
+ MERCHANT_INVENTORIES = new EnumMap<>(Version.class);
+ MERCHANT_INVENTORIES.put(Version.V1_16_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_16_5.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_17_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_17_1.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_18_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_18_2.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_19_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_19_4.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_20_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_0.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_20_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_1.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_20_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_2.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_20_3_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_3.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_20_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_5.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_20_6,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_6.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_21_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_0.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_21_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_1.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_21_2_3,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_21_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_4.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_21_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_5.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_21_6_8,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_6_8.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_21_9_10,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_9_10.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V1_21_11,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_11.MerchantInventoryImpl.class);
+ MERCHANT_INVENTORIES.put(Version.V26_1,
+ com.github.stefvanschie.inventoryframework.nms.v26_1.MerchantInventoryImpl.class);
SMITHING_TABLE_INVENTORIES = new EnumMap<>(Version.class);
- SMITHING_TABLE_INVENTORIES.put(Version.V1_16_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R1.SmithingTableInventoryImpl.class);
- SMITHING_TABLE_INVENTORIES.put(Version.V1_16_R2,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R2.SmithingTableInventoryImpl.class);
- SMITHING_TABLE_INVENTORIES.put(Version.V1_16_R3,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R3.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_19_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_19_4.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_20_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_0.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_20_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_1.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_20_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_2.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_20_3_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_3.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_20_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_5.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_20_6,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_6.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_21_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_0.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_21_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_1.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_21_2_3,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_21_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_4.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_21_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_5.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_21_6_8,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_6_8.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_21_9_10,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_9_10.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V1_21_11,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_11.SmithingTableInventoryImpl.class);
+ SMITHING_TABLE_INVENTORIES.put(Version.V26_1,
+ com.github.stefvanschie.inventoryframework.nms.v26_1.SmithingTableInventoryImpl.class);
+
+ LEGACY_SMITHING_TABLE_INVENTORIES = new EnumMap<>(Version.class);
+ LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_16_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_16_5.SmithingTableInventoryImpl.class);
+ LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_17_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_17_1.SmithingTableInventoryImpl.class);
+ LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_18_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_18_2.SmithingTableInventoryImpl.class);
+ LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_19_4,
+ LegacySmithingTableInventoryImpl.class);
STONECUTTER_INVENTORIES = new EnumMap<>(Version.class);
- STONECUTTER_INVENTORIES.put(Version.V1_14_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_14_R1.StonecutterInventoryImpl.class);
- STONECUTTER_INVENTORIES.put(Version.V1_15_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_15_R1.StonecutterInventoryImpl.class);
- STONECUTTER_INVENTORIES.put(Version.V1_16_R1,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R1.StonecutterInventoryImpl.class);
- STONECUTTER_INVENTORIES.put(Version.V1_16_R2,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R2.StonecutterInventoryImpl.class);
- STONECUTTER_INVENTORIES.put(Version.V1_16_R3,
- com.github.stefvanschie.inventoryframework.nms.v1_16_R3.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_16_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_16_5.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_17_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_17_1.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_18_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_18_2.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_19_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_19_4.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_20_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_0.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_20_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_1.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_20_2,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_2.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_20_3_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_3.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_20_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_5.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_20_6,
+ com.github.stefvanschie.inventoryframework.nms.v1_20_6.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_21_0,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_0.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_21_1,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_1.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_21_2_3,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_2_3.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_21_4,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_4.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_21_5,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_5.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_21_6_8,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_6_8.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_21_9_10,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_9_10.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V1_21_11,
+ com.github.stefvanschie.inventoryframework.nms.v1_21_11.StonecutterInventoryImpl.class);
+ STONECUTTER_INVENTORIES.put(Version.V26_1,
+ com.github.stefvanschie.inventoryframework.nms.v26_1.StonecutterInventoryImpl.class);
}
}
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/gui/GuiComponentTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/gui/GuiComponentTest.java
new file mode 100644
index 000000000..5779143b4
--- /dev/null
+++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/gui/GuiComponentTest.java
@@ -0,0 +1,94 @@
+package com.github.stefvanschie.inventoryframework.gui;
+
+import com.github.stefvanschie.inventoryframework.pane.*;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class GuiComponentTest {
+
+ @Test
+ void testConstructor() {
+ assertThrows(IllegalArgumentException.class, () -> new GuiComponent(-1, 1));
+ assertThrows(IllegalArgumentException.class, () -> new GuiComponent(1, -1));
+ assertThrows(IllegalArgumentException.class, () -> new GuiComponent(-1, -1));
+ assertDoesNotThrow(() -> new GuiComponent(0, 0));
+ }
+
+ @Test
+ void testAddPane() {
+ GuiComponent guiComponent = new GuiComponent(0, 0);
+
+ guiComponent.addPane(Slot.fromXY(0, 0), new StaticPane(1, 1));
+
+ List panes = guiComponent.getPanes();
+
+ assertEquals(1, panes.size());
+ assertTrue(panes.get(0) instanceof StaticPane);
+ }
+
+ @Test
+ void testCopy() {
+ GuiComponent original = new GuiComponent(0, 0);
+
+ original.addPane(Slot.fromXY(0, 0), new StaticPane(1, 1));
+ original.addPane(Slot.fromXY(0, 0), new OutlinePane(1, 1));
+
+ GuiComponent copy = original.copy();
+
+ assertNotSame(original, copy);
+
+ assertEquals(original.getLength(), copy.getLength());
+ assertEquals(original.getHeight(), copy.getHeight());
+ assertEquals(original.getPanes().size(), copy.getPanes().size());
+ }
+
+ @Test
+ void testExcludeRowsValid() {
+ GuiComponent original = new GuiComponent(0, 6);
+
+ original.addPane(Slot.fromXY(0, 0), new StaticPane(1, 1));
+ original.addPane(Slot.fromXY(0, 0), new OutlinePane(1, 1));
+ original.addPane(Slot.fromXY(0, 0), new PaginatedPane(1, 1));
+ original.addPane(Slot.fromXY(0, 0), new MasonryPane(1, 1));
+
+ GuiComponent shrunk = original.excludeRows(4, 4);
+
+ assertEquals(5, shrunk.getHeight());
+ assertEquals(original.getPanes().size(), shrunk.getPanes().size());
+
+ for (Pane pane : original.getPanes()) {
+ assertTrue(shrunk.getPanes().contains(pane));
+ }
+ }
+
+ @Test
+ void testExcludeRowsInvalid() {
+ GuiComponent guiComponent = new GuiComponent(0, 5);
+
+ assertThrows(IllegalArgumentException.class, () -> guiComponent.excludeRows(8, 8));
+ }
+
+ @Test
+ void testGetPanesEmptyWhenNone() {
+ assertEquals(0, new GuiComponent(0, 0).getPanes().size());
+ }
+
+ @Test
+ void testGetPanesSorted() {
+ GuiComponent guiComponent = new GuiComponent(0, 0);
+
+ guiComponent.addPane(Slot.fromXY(0, 0), new StaticPane(1, 1, Pane.Priority.HIGHEST));
+ guiComponent.addPane(Slot.fromXY(0, 0), new OutlinePane(1, 1, Pane.Priority.LOW));
+ guiComponent.addPane(Slot.fromXY(0, 0), new PaginatedPane(1, 1, Pane.Priority.MONITOR));
+
+ List panes = guiComponent.getPanes();
+
+ assertEquals(Pane.Priority.LOW, panes.get(0).getPriority());
+ assertEquals(Pane.Priority.HIGHEST, panes.get(1).getPriority());
+ assertEquals(Pane.Priority.MONITOR, panes.get(2).getPriority());
+ }
+}
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/gui/InventoryComponentTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/gui/InventoryComponentTest.java
deleted file mode 100644
index e9f58386e..000000000
--- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/gui/InventoryComponentTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.github.stefvanschie.inventoryframework.gui;
-
-import com.github.stefvanschie.inventoryframework.pane.*;
-import org.junit.jupiter.api.Test;
-
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-public class InventoryComponentTest {
-
- @Test
- void testConstructor() {
- assertThrows(IllegalArgumentException.class, () -> new InventoryComponent(-1, 1));
- assertThrows(IllegalArgumentException.class, () -> new InventoryComponent(1, -1));
- assertThrows(IllegalArgumentException.class, () -> new InventoryComponent(-1, -1));
- assertDoesNotThrow(() -> new InventoryComponent(0, 0));
- }
-
- @Test
- void testAddPane() {
- InventoryComponent inventoryComponent = new InventoryComponent(0, 0);
-
- inventoryComponent.addPane(new StaticPane(1, 1));
-
- List panes = inventoryComponent.getPanes();
-
- assertEquals(1, panes.size());
- assertTrue(panes.get(0) instanceof StaticPane);
- }
-
- @Test
- void testCopy() {
- InventoryComponent original = new InventoryComponent(0, 0);
-
- original.addPane(new StaticPane(1, 1));
- original.addPane(new OutlinePane(1, 1));
-
- InventoryComponent copy = original.copy();
-
- assertNotSame(original, copy);
-
- assertEquals(original.getLength(), copy.getLength());
- assertEquals(original.getHeight(), copy.getHeight());
- assertEquals(original.getPanes().size(), copy.getPanes().size());
- }
-
- @Test
- void testExcludeRowsValid() {
- InventoryComponent original = new InventoryComponent(0, 6);
-
- original.addPane(new StaticPane(1, 1));
- original.addPane(new OutlinePane(1, 1));
- original.addPane(new PaginatedPane(1, 1));
- original.addPane(new MasonryPane(1, 1));
-
- InventoryComponent shrunk = original.excludeRows(4, 4);
-
- assertEquals(5, shrunk.getHeight());
- assertEquals(original.getPanes().size(), shrunk.getPanes().size());
-
- for (Pane pane : original.getPanes()) {
- assertTrue(shrunk.getPanes().contains(pane));
- }
- }
-
- @Test
- void testExcludeRowsInvalid() {
- InventoryComponent inventoryComponent = new InventoryComponent(0, 5);
-
- //noinspection ResultOfMethodCallIgnored
- assertThrows(IllegalArgumentException.class, () -> inventoryComponent.excludeRows(8, 8));
- }
-
- @Test
- void testGetPanesEmptyWhenNone() {
- assertEquals(0, new InventoryComponent(0, 0).getPanes().size());
- }
-
- @Test
- void testGetPanesSorted() {
- InventoryComponent inventoryComponent = new InventoryComponent(0, 0);
-
- inventoryComponent.addPane(new StaticPane(0, 0, 1, 1, Pane.Priority.HIGHEST));
- inventoryComponent.addPane(new OutlinePane(0, 0, 1, 1, Pane.Priority.LOW));
- inventoryComponent.addPane(new PaginatedPane(0, 0, 1, 1, Pane.Priority.MONITOR));
-
- List panes = inventoryComponent.getPanes();
-
- assertEquals(Pane.Priority.LOW, panes.get(0).getPriority());
- assertEquals(Pane.Priority.HIGHEST, panes.get(1).getPriority());
- assertEquals(Pane.Priority.MONITOR, panes.get(2).getPriority());
- }
-
- @Test
- void testGetSize() {
- assertEquals(30, new InventoryComponent(3, 10).getSize());
- }
-}
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/MasonryPaneTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/MasonryPaneTest.java
index e1f4a9ab9..6df745ee6 100644
--- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/MasonryPaneTest.java
+++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/MasonryPaneTest.java
@@ -9,7 +9,7 @@ public class MasonryPaneTest {
@Test
void testCopy() {
- MasonryPane original = new MasonryPane(7, 5, 1, 1, Pane.Priority.LOW);
+ MasonryPane original = new MasonryPane(1, 1, Pane.Priority.LOW);
original.setVisible(false);
original.setOrientation(Orientable.Orientation.VERTICAL);
@@ -23,8 +23,6 @@ void testCopy() {
assertNotSame(original, copy);
- assertEquals(original.getX(), copy.getX());
- assertEquals(original.getY(), copy.getY());
assertEquals(original.getLength(), copy.getLength());
assertEquals(original.getHeight(), copy.getHeight());
assertEquals(original.getPriority(), copy.getPriority());
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/OutlinePaneTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/OutlinePaneTest.java
index 3522c1185..bf8d01826 100644
--- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/OutlinePaneTest.java
+++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/OutlinePaneTest.java
@@ -15,7 +15,7 @@ void testApplyMaskInvalidDimensions() {
@Test
void testCopy() {
- OutlinePane original = new OutlinePane(8, 5, 1, 1, Pane.Priority.HIGHEST);
+ OutlinePane original = new OutlinePane(1, 1, Pane.Priority.HIGHEST);
original.setVisible(false);
original.setOrientation(Orientable.Orientation.VERTICAL);
original.setRotation(180);
@@ -24,13 +24,12 @@ void testCopy() {
original.flipHorizontally(true);
original.flipVertically(true);
original.applyMask(new Mask("0"));
+ original.align(OutlinePane.Alignment.CENTER);
OutlinePane copy = original.copy();
assertNotSame(original, copy);
- assertEquals(original.getX(), copy.getX());
- assertEquals(original.getY(), copy.getY());
assertEquals(original.getLength(), copy.getLength());
assertEquals(original.getHeight(), copy.getHeight());
assertEquals(original.getPriority(), copy.getPriority());
@@ -42,6 +41,7 @@ void testCopy() {
assertEquals(original.isFlippedHorizontally(), copy.isFlippedHorizontally());
assertEquals(original.isFlippedVertically(), copy.isFlippedVertically());
assertEquals(original.getMask(), copy.getMask());
+ assertEquals(original.getAlignment(), copy.getAlignment());
assertEquals(original.getUUID(), copy.getUUID());
}
}
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPaneTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPaneTest.java
index 6be43125d..fe91c635d 100644
--- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPaneTest.java
+++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PaginatedPaneTest.java
@@ -1,25 +1,151 @@
package com.github.stefvanschie.inventoryframework.pane;
-import com.github.stefvanschie.inventoryframework.font.util.Font;
-import com.github.stefvanschie.inventoryframework.pane.component.Label;
-import com.github.stefvanschie.inventoryframework.pane.component.PercentageBar;
-import com.github.stefvanschie.inventoryframework.pane.component.ToggleButton;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
import static org.junit.jupiter.api.Assertions.*;
public class PaginatedPaneTest {
+ @Test
+ void testAddPageEmpty() {
+ PaginatedPane paginatedPane = new PaginatedPane(1, 1);
+
+ StaticPane staticPane = new StaticPane(1, 1);
+
+ assertDoesNotThrow(() -> {
+ paginatedPane.addPage(Slot.fromXY(0, 0), staticPane);
+
+ Collection panes = paginatedPane.getPanes(0);
+
+ assertEquals(1, panes.size());
+ assertSame(staticPane, panes.iterator().next());
+ });
+ }
+
+ @Test
+ void testAddPageNotEmpty() {
+ PaginatedPane paginatedPane = new PaginatedPane(1, 1);
+
+ StaticPane staticPane1 = new StaticPane(1, 1);
+ StaticPane staticPane2 = new StaticPane(1, 1);
+
+ paginatedPane.addPane(0, Slot.fromXY(0, 0), staticPane1);
+
+ assertDoesNotThrow(() -> {
+ paginatedPane.addPage(Slot.fromXY(0, 0), staticPane2);
+
+ Collection panes = paginatedPane.getPanes(1);
+
+ assertEquals(1, panes.size());
+ assertSame(staticPane2, panes.iterator().next());
+ });
+ }
+
+ @Test
+ void testAddPaneNegative() {
+ PaginatedPane paginatedPane = new PaginatedPane( 1, 1);
+
+ StaticPane staticPane = new StaticPane(1, 1);
+
+ assertThrows(IllegalArgumentException.class, () -> paginatedPane.addPane(-1, Slot.fromXY(0, 0), staticPane));
+ }
+
+ @Test
+ void testAddPaneExisting() {
+ PaginatedPane paginatedPane = new PaginatedPane(1, 1);
+
+ StaticPane staticPane1 = new StaticPane(1, 1);
+ StaticPane staticPane2 = new StaticPane(1, 1);
+
+ Set super Pane> elements = new HashSet<>();
+
+ elements.add(staticPane1);
+ elements.add(staticPane2);
+
+ paginatedPane.addPane(0, Slot.fromXY(0, 0), staticPane1);
+
+ assertDoesNotThrow(() -> {
+ paginatedPane.addPane(0, Slot.fromXY(0, 0), staticPane2);
+
+ Collection panes = paginatedPane.getPanes(0);
+
+ assertEquals(elements.size(), panes.size());
+ assertTrue(elements.containsAll(panes));
+ });
+ }
+
+ @Test
+ void testAddPaneAfter() {
+ PaginatedPane paginatedPane = new PaginatedPane(1, 1);
+
+ StaticPane staticPane1 = new StaticPane(1, 1);
+ StaticPane staticPane2 = new StaticPane(1, 1);
+
+ paginatedPane.addPane(0, Slot.fromXY(0, 0), staticPane1);
+
+ assertDoesNotThrow(() -> {
+ paginatedPane.addPane(1, Slot.fromXY(0, 0), staticPane2);
+
+ Collection panes0 = paginatedPane.getPanes(0);
+
+ assertEquals(1, panes0.size());
+ assertEquals(staticPane1, panes0.iterator().next());
+
+ Collection panes1 = paginatedPane.getPanes(1);
+
+ assertEquals(1, panes1.size());
+ assertEquals(staticPane2, panes1.iterator().next());
+ });
+ }
+
+ @Test
+ void testAddPaneBeyond() {
+ PaginatedPane paginatedPane = new PaginatedPane(1, 1);
+
+ StaticPane staticPane1 = new StaticPane(1, 1);
+ StaticPane staticPane2 = new StaticPane(1, 1);
+
+ paginatedPane.addPane(0, Slot.fromXY(0, 0), staticPane1);
+
+ assertThrows(IllegalArgumentException.class, () -> paginatedPane.addPane(2, Slot.fromXY(0, 0), staticPane2));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {-1, 0})
+ void testSetPageOutside(int index) {
+ PaginatedPane paginatedPane = new PaginatedPane(1, 1);
+
+ assertThrows(ArrayIndexOutOfBoundsException.class, () -> paginatedPane.setPage(index));
+ }
+
+ @Test
+ void testSetPage() {
+ PaginatedPane paginatedPane = new PaginatedPane(1, 1);
+
+ StaticPane staticPane1 = new StaticPane(1, 1);
+
+ paginatedPane.addPage(Slot.fromXY(0, 0), staticPane1);
+
+ assertDoesNotThrow(() -> paginatedPane.setPage(0));
+ }
+
@Test
void testCopy() {
- PaginatedPane original = new PaginatedPane(5, 5, 4, 1, Pane.Priority.NORMAL);
+ PaginatedPane original = new PaginatedPane(4, 1, Pane.Priority.NORMAL);
original.setVisible(false);
- original.addPane(0, new OutlinePane(1, 1));
- original.addPane(1, new OutlinePane(1, 1));
- original.addPane(2, new PaginatedPane(1, 1));
- original.addPane(3, new PaginatedPane(1, 1));
- original.addPane(4, new OutlinePane(1, 1));
+ original.addPane(0, Slot.fromXY(0, 0), new OutlinePane(1, 1));
+ original.addPane(1, Slot.fromXY(0, 0), new OutlinePane(1, 1));
+ original.addPane(2, Slot.fromXY(0, 0), new PaginatedPane(1, 1));
+ original.addPane(3, Slot.fromXY(0, 0), new PaginatedPane(1, 1));
+ original.addPane(4, Slot.fromXY(0, 0), new OutlinePane(1, 1));
original.setPage(4);
@@ -27,8 +153,6 @@ void testCopy() {
assertNotSame(original, copy);
- assertEquals(original.getX(), copy.getX());
- assertEquals(original.getY(), copy.getY());
assertEquals(original.getLength(), copy.getLength());
assertEquals(original.getHeight(), copy.getHeight());
assertEquals(original.getPriority(), copy.getPriority());
@@ -37,4 +161,28 @@ void testCopy() {
assertEquals(original.getPages(), copy.getPages());
assertEquals(original.getUUID(), copy.getUUID());
}
+
+ @Test
+ void testDeletePageExists() {
+ PaginatedPane pane = new PaginatedPane(1, 1);
+
+ StaticPane staticPane = new StaticPane(1, 1);
+
+ pane.addPane(0, Slot.fromXY(0, 0), new StaticPane(1, 1));
+ pane.addPane(1, Slot.fromXY(0, 0), staticPane);
+
+ pane.deletePage(0);
+
+ assertEquals(1, pane.getPages());
+ assertEquals(1, pane.getPanes(0).size());
+ assertSame(staticPane, pane.getPanes(0).toArray(new Pane[0])[0]);
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {-1, 0})
+ void testDeletePageNotExists(int index) {
+ PaginatedPane pane = new PaginatedPane(1, 1);
+
+ assertDoesNotThrow(() -> pane.deletePage(index));
+ }
}
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PatternPaneTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PatternPaneTest.java
index a9f2fc857..df057b811 100644
--- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PatternPaneTest.java
+++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/PatternPaneTest.java
@@ -27,7 +27,7 @@ void testApplyPatternInvalidDimensions() {
@Test
void testCopy() {
- PatternPane original = new PatternPane(8, 5, 1, 1, Pane.Priority.HIGHEST, new Pattern("1"));
+ PatternPane original = new PatternPane(1, 1, Pane.Priority.HIGHEST, new Pattern("1"));
original.setVisible(false);
original.setRotation(180);
original.flipHorizontally(true);
@@ -37,8 +37,6 @@ void testCopy() {
assertNotSame(original, copy);
- assertEquals(original.getX(), copy.getX());
- assertEquals(original.getY(), copy.getY());
assertEquals(original.getLength(), copy.getLength());
assertEquals(original.getHeight(), copy.getHeight());
assertEquals(original.getPriority(), copy.getPriority());
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/StaticPaneTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/StaticPaneTest.java
index 38dfec05c..e87fdcadd 100644
--- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/StaticPaneTest.java
+++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/StaticPaneTest.java
@@ -1,5 +1,6 @@
package com.github.stefvanschie.inventoryframework.pane;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@@ -8,7 +9,7 @@ public class StaticPaneTest {
@Test
void testCopy() {
- StaticPane original = new StaticPane(5, 1, 1, 1, Pane.Priority.MONITOR);
+ StaticPane original = new StaticPane(1, 1, Pane.Priority.MONITOR);
original.setVisible(false);
original.setRotation(90);
original.flipHorizontally(false);
@@ -18,8 +19,6 @@ void testCopy() {
assertNotSame(original, copy);
- assertEquals(original.getX(), copy.getX());
- assertEquals(original.getY(), copy.getY());
assertEquals(original.getLength(), copy.getLength());
assertEquals(original.getHeight(), copy.getHeight());
assertEquals(original.getPriority(), copy.getPriority());
@@ -29,4 +28,18 @@ void testCopy() {
assertEquals(original.isFlippedVertically(), copy.isFlippedVertically());
assertEquals(original.getUUID(), copy.getUUID());
}
+
+ @Test
+ void testRemoveItemCoordinates() {
+ StaticPane pane = new StaticPane(1, 1);
+
+ assertDoesNotThrow(() -> pane.removeItem(0, 0));
+ }
+
+ @Test
+ void testRemoveItemSlot() {
+ StaticPane pane = new StaticPane(1, 1);
+
+ assertDoesNotThrow(() -> pane.removeItem(Slot.fromXY(0, 0)));
+ }
}
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButtonTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButtonTest.java
index ee0f8978e..fbef452b4 100644
--- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButtonTest.java
+++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/component/CycleButtonTest.java
@@ -9,7 +9,7 @@ public class CycleButtonTest {
@Test
void testCopy() {
- CycleButton original = new CycleButton(6, 2, 2, 3, Pane.Priority.HIGH);
+ CycleButton original = new CycleButton(2, 3, Pane.Priority.HIGH);
original.setVisible(true);
original.addPane(new CycleButton(1, 1));
@@ -18,8 +18,6 @@ void testCopy() {
assertNotSame(original, copy);
- assertEquals(original.getX(), copy.getX());
- assertEquals(original.getY(), copy.getY());
assertEquals(original.getLength(), copy.getLength());
assertEquals(original.getHeight(), copy.getHeight());
assertEquals(original.getPriority(), copy.getPriority());
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/GuiItemContainerTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/GuiItemContainerTest.java
new file mode 100644
index 000000000..cd6532624
--- /dev/null
+++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/GuiItemContainerTest.java
@@ -0,0 +1,65 @@
+package com.github.stefvanschie.inventoryframework.pane.util;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class GuiItemContainerTest {
+
+ @Test
+ void testExcludeRowsValid() {
+ GuiItemContainer container = new GuiItemContainer(8, 4);
+ GuiItemContainer shrunk = container.excludeRows(0, 2);
+
+ assertEquals(1, shrunk.getHeight());
+ assertNotSame(shrunk, container);
+ }
+
+ @Test
+ void testExcludeRowsInvalid() {
+ GuiItemContainer container = new GuiItemContainer(9, 1);
+
+ assertThrows(IllegalArgumentException.class, () -> container.excludeRows(4, 6));
+ }
+
+ @Test
+ void testCopy() {
+ GuiItemContainer original = new GuiItemContainer(6, 7);
+
+ GuiItemContainer copy = original.copy();
+
+ assertNotSame(original, copy);
+
+ assertEquals(original.getLength(), copy.getLength());
+ assertEquals(original.getHeight(), copy.getHeight());
+ }
+
+ @Test
+ void testHasItem() {
+ assertFalse(new GuiItemContainer(8, 5).hasItem());
+ }
+
+ @Test
+ void testGetItemInside() {
+ assertNull(new GuiItemContainer(8, 1).getItem(3, 0));
+ }
+
+ @Test
+ void testGetItemOutside() {
+ GuiItemContainer container = new GuiItemContainer(8, 1);
+
+ assertThrows(IllegalArgumentException.class, () -> container.getItem(7, 4));
+ }
+
+ @Test
+ void testHasItemInside() {
+ assertFalse(new GuiItemContainer(2, 7).hasItem(1, 4));
+ }
+
+ @Test
+ void testHasItemOutside() {
+ GuiItemContainer container = new GuiItemContainer(2, 5);
+
+ assertThrows(IllegalArgumentException.class, () -> container.hasItem(8, 3));
+ }
+}
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/MaskTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/MaskTest.java
index 3545e05ab..189af67ef 100644
--- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/MaskTest.java
+++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/MaskTest.java
@@ -39,6 +39,11 @@ void testGetColumn() {
"10",
"00"
).getColumn(0));
+
+ assertArrayEquals(new boolean[] {true, false}, new Mask(
+ "1",
+ "0"
+ ).getColumn(0));
}
@Test
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/SlotTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/SlotTest.java
new file mode 100644
index 000000000..2ad037ef8
--- /dev/null
+++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/pane/util/SlotTest.java
@@ -0,0 +1,23 @@
+package com.github.stefvanschie.inventoryframework.pane.util;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class SlotTest {
+
+ @Test
+ void testEquals() {
+ assertEquals(Slot.fromXY(0, 0), Slot.fromXY(0, 0));
+ assertNotEquals(Slot.fromXY(0, 1), Slot.fromXY(1, 0));
+
+ assertEquals(Slot.fromIndex(0), Slot.fromIndex(0));
+ assertNotEquals(Slot.fromIndex(0), Slot.fromIndex(1));
+ }
+
+ @Test
+ void testHashCode() {
+ assertEquals(Slot.fromXY(0, 0).hashCode(), Slot.fromXY(0, 0).hashCode());
+ assertEquals(Slot.fromIndex(0).hashCode(), Slot.fromIndex(0).hashCode());
+ }
+}
diff --git a/IF/src/test/java/com/github/stefvanschie/inventoryframework/util/PaginatedPaneTest.java b/IF/src/test/java/com/github/stefvanschie/inventoryframework/util/PaginatedPaneTest.java
index 1f22d9929..70021909d 100644
--- a/IF/src/test/java/com/github/stefvanschie/inventoryframework/util/PaginatedPaneTest.java
+++ b/IF/src/test/java/com/github/stefvanschie/inventoryframework/util/PaginatedPaneTest.java
@@ -3,6 +3,7 @@
import com.github.stefvanschie.inventoryframework.pane.PaginatedPane;
import com.github.stefvanschie.inventoryframework.pane.Pane;
import com.github.stefvanschie.inventoryframework.pane.StaticPane;
+import com.github.stefvanschie.inventoryframework.pane.util.Slot;
import org.junit.jupiter.api.Test;
import java.util.Collection;
@@ -15,7 +16,6 @@ public class PaginatedPaneTest {
void testGetPanesNonExistentPage() {
PaginatedPane pane = new PaginatedPane(1, 1);
- //noinspection ResultOfMethodCallIgnored
assertThrows(IllegalArgumentException.class, () -> pane.getPanes(0));
}
@@ -27,9 +27,9 @@ void testGetPanesCollectionContents() {
StaticPane pane1 = new StaticPane(1, 1);
StaticPane pane2 = new StaticPane(1, 1);
- paginatedPane.addPane(0, pane0);
- paginatedPane.addPane(0, pane1);
- paginatedPane.addPane(0, pane2);
+ paginatedPane.addPane(0, Slot.fromXY(0, 0), pane0);
+ paginatedPane.addPane(0, Slot.fromXY(0, 0), pane1);
+ paginatedPane.addPane(0, Slot.fromXY(0, 0), pane2);
Collection panes = paginatedPane.getPanes(0);
assertTrue(panes.contains(pane0));
@@ -45,9 +45,9 @@ void testGetPanesCollectionSize() {
StaticPane pane1 = new StaticPane(1, 1);
StaticPane pane2 = new StaticPane(1, 1);
- paginatedPane.addPane(0, pane0);
- paginatedPane.addPane(0, pane1);
- paginatedPane.addPane(0, pane2);
+ paginatedPane.addPane(0, Slot.fromXY(0, 0), pane0);
+ paginatedPane.addPane(0, Slot.fromXY(0, 0), pane1);
+ paginatedPane.addPane(0, Slot.fromXY(0, 0), pane2);
assertEquals(3, paginatedPane.getPanes(0).size());
}
diff --git a/README.md b/README.md
index a6d6353e1..57476a883 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# IF
-*This framework works for Minecraft versions 1.14-1.16*
+*This framework works for Minecraft versions 1.16.5, 1.17.1, 1.18.2, 1.19.4, 1.20-1.21, and 26.1*
An inventory framework for managing GUIs
@@ -14,7 +14,7 @@ To add this project as a dependency to your pom.xml, add the following to your p
com.github.stefvanschie.inventoryframework
IF
- 0.9.8
+ 0.12.0
```
The project is in the Central Repository, so specifying a repository is not needed.
@@ -24,7 +24,7 @@ Now in order to shade the project into your project, add the following to your p
org.apache.maven.plugins
maven-shade-plugin
- 3.2.2
+ 3.5.2
${project.build.directory}/dependency-reduced-pom.xml
@@ -50,7 +50,7 @@ Replace [YOUR PACKAGE] with the top-level package of your project.
To add this project as a dependency for your Gradle project, make sure your `dependencies` section of your build.gradle looks like the following:
```Groovy
dependencies {
- compile 'com.github.stefvanschie.inventoryframework:IF:0.9.8'
+ implementation 'com.github.stefvanschie.inventoryframework:IF:0.12.0'
// ...
}
```
@@ -63,7 +63,10 @@ repositories {
```
In order to include the project in your own project, you will need to use the `shadowJar` plugin. If you don't have it already, add the following to the top of your file:
```Groovy
-apply plugin: 'com.github.johnrengelman.shadow'
+plugins {
+ // ...
+ id "com.github.johnrengelman.shadow" version "7.1.2"
+}
```
To relocate the project's classes to your own namespace, add the following, with [YOUR PACKAGE] being the top-level package of your project:
```Groovy
@@ -72,11 +75,137 @@ shadowJar {
}
```
+## Dependency via plugin.yml
+IF does **not** support declaring the dependency via the libraries section in the plugin.yml. Please make use of a build tool as described above to use IF as a dependency.
+
## Building from source
-If you want to build this project from source, run the following from Git Bash:
+If you want to build this project from source, run the following:
git clone https://github.com/stefvanschie/IF.git
- cd IF
- mvn clean package
-The build can then be found in /IF/target/.
+This will clone this repository to your device. This project relies on NMS, for which the dependencies are not available online. Because of this, you'll need to follow additional steps to obtain all these dependencies locally.
+
+### Installing Paper manually
+For versions 1.15-1.16, we have to manually install Paper. Run the following scripts for each version to install the dependencies locally. Running these commands generate additional files in the folder where you execute them. To ensure that you don't accidentallly overwrite other files, execute this in an empty folder. The files that get created can be deleted afterwards (either after installing a single version or after installing all of them), since they're no longer necessary.
+
+#### 1.16.5
+```
+wget https://api.papermc.io/v2/projects/paper/versions/1.16.5/builds/794/downloads/paper-1.16.5-794.jar -O paperclip/paper-1.16.5.jar
+java -jar paper-1.16.5.jar
+mvn install:install-file -Dfile=cache/patched_1.16.5.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.5-R0.1-SNAPSHOT" -Dpackaging="jar" -DgeneratePom="true"
+```
+
+### Installing Paper via the maven plugin
+For versions 1.17-1.20.4, we use Paper via the [paper-nms-maven-plugin](https://github.com/Alvinn8/paper-nms-maven-plugin). To install these versions locally, we must run a few maven commands. These commands should be ran in the root directory of the project.
+```
+mvn paper-nms:init -pl nms/1_17_1
+mvn paper-nms:init -pl nms/1_18_2
+mvn paper-nms:init -pl nms/1_19_4
+mvn paper-nms:init -pl nms/1_20_0-1
+mvn paper-nms:init -pl nms/1_20_2
+mvn paper-nms:init -pl nms/1_20_3-4
+```
+
+### Installing Spigot via BuildTools
+For versions 1.20.5-1.21.11 and 26.1, we use BuildTools. To install these versions, we run the following commands.
+```
+wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O BuildTools.jar
+
+git clone https://hub.spigotmc.org/stash/scm/spigot/bukkit.git Bukkit
+cd Bukkit
+git checkout 304e83eb384c338546aa96eea51388e0e8407e26
+cd ..
+
+git clone https://hub.spigotmc.org/stash/scm/spigot/craftbukkit.git CraftBukkit
+cd CraftBukkit
+git checkout 91b1fc3f1cf89e2591367dca1fa7362fe376f289
+cd ..
+
+git clone https://hub.spigotmc.org/stash/scm/spigot/spigot.git Spigot
+cd Spigot
+git checkout b698b49caf14f97a717afd67e13fd7ac59f51089
+cd ..
+
+git clone https://hub.spigotmc.org/stash/scm/spigot/builddata.git BuildData
+cd BuildData
+git checkout a7f7c2118b877fde4cf0f32f1f730ffcdee8e9ee
+cd ..
+
+java -jar BuildTools.jar --remapped --disable-java-check --dont-update
+java -jar BuildTools.jar --rev 1.20.6 --remapped --disable-java-check
+
+cd Bukkit
+git checkout 2ec53f498e32b3af989cb24672fc54dfab087154
+cd ..
+
+cd CraftBukkit
+git checkout 8ee6fd1b8db9896590aa321d0199453de1fc35db
+cd ..
+
+cd Spigot
+git checkout fb8fb722a327a2f9f097f2ded700ac5de8157408
+cd ..
+
+cd BuildData
+git checkout ae1e7b1e31cd3a3892bb05a6ccdcecc48c73c455
+cd ..
+
+java -jar BuildTools.jar --remapped --disable-java-check --dont-update
+java -jar BuildTools.jar --rev 1.21.1 --remapped --disable-java-check
+java -jar BuildTools.jar --rev 1.21.3 --remapped --disable-java-check
+java -jar BuildTools.jar --rev 1.21.4 --remapped --disable-java-check
+java -jar BuildTools.jar --rev 1.21.5 --remapped --disable-java-check
+java -jar BuildTools.jar --rev 1.21.8 --remapped --disable-java-check
+java -jar BuildTools.jar --rev 1.21.10 --remapped --disable-java-check
+java -jar BuildTools.jar --rev 1.21.11 --remapped --disable-java-check
+java -jar BuildTools.jar --rev 26.1 --remapped --disable-java-check
+```
+
+Your environment is now set up correctly. To create a build, run the following inside the root folder of the project.
+```
+mvn clean package
+```
+Your build is now available in the /IF/target folder.
+
+## Adventure support
+
+IF supports [Adventure](https://github.com/KyoriPowered/adventure), but does not shade it in itself.
+The use of Adventure `Component`s instead of legacy `String`s is completely optional.
+If you do not wish to use Adventure you can safely ignore all `TextHolder` related methods.
+
+### What is Adventure?
+
+Adventure is a library that adds proper modern text support to Minecraft.
+Modern text is represented using bungee-chat and `BaseComponent` instances in Spigot.
+Adventure is an alternative to bungee-chat and offers more features.
+
+### Using Adventure on 1.16.5+ Paper
+
+You don't need to import/shade anything for Adventure support in this case!
+
+*Note: Paper only supports Adventure on build 473 and above. If you aren't running months old builds, then you are fine.*
+
+### Using Adventure on Spigot and older Paper
+
+On Spigot Adventure isn't included in the server, therefore you have to shade and relocate it yourself.
+The following dependencies need to be imported and shaded:
+- adventure-api
+- adventure-platform-bukkit
+
+Please consult the [Adventure documentation](https://docs.adventure.kyori.net/) for more information.
+
+### How to use Adventure `Component`s
+
+Example of migration from legacy `String` to Adventure `Component`:
+ - legacy: `namedGui.setTitle("My Title!");`
+ - Adventure: `namedGui.setTitle(ComponentHolder.of(Component.text("My Title!")));`
+
+We apologize for the boilerplate (the `ComponentHolder.of(...)` call), but that was the only way to not make IF hard-depend on Adventure.
+
+Full Adventure support is only achieved when your server natively supports Adventure (it is running Paper) and your plugin depends on Paper (instead of Spigot).
+In other words, you won't benefit from Adventure as much if you use Spigot instead of Paper.
+This is because when Adventure is relocated we have to convert everything back to legacy `String`s before passing them to the Bukkit API.
+
+---
+
+NOT AN OFFICIAL MINECRAFT PRODUCT. NOT APPROVED BY OR ASSOCIATED WITH MOJANG OR MICROSOFT.
diff --git a/adventure-support/pom.xml b/adventure-support/pom.xml
new file mode 100644
index 000000000..efceee2e2
--- /dev/null
+++ b/adventure-support/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ IF-parent
+ com.github.stefvanschie.inventoryframework
+ 0.12.0
+
+ 4.0.0
+
+ adventure-support
+
+
+ true
+
+
+
+
+ papermc
+ https://repo.papermc.io/repository/maven-public/
+
+
+
+
+
+ com.destroystokyo.paper
+ paper-api
+ 1.16.5-R0.1-SNAPSHOT
+ provided
+
+
+ net.kyori
+ adventure-api
+ ${adventure.version}
+ provided
+
+
+
diff --git a/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ComponentHolder.java b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ComponentHolder.java
new file mode 100644
index 000000000..3d6a4b587
--- /dev/null
+++ b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ComponentHolder.java
@@ -0,0 +1,176 @@
+package com.github.stefvanschie.inventoryframework.adventuresupport;
+
+import com.google.gson.JsonElement;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import org.apache.commons.lang.Validate;
+import org.bukkit.Material;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Objects;
+
+/**
+ * Wrapper of an Adventure {@link Component}.
+ *
+ * @since 0.10.0
+ */
+public abstract class ComponentHolder extends TextHolder {
+
+ /**
+ * Whether the server platform natively supports Adventure.
+ * A null value indicates that we don't yet know this: it hasn't been determined yet.
+ * This field should not be used directly, use {@link #isNativeAdventureSupport()} instead.
+ */
+ @Nullable
+ private static Boolean nativeAdventureSupport;
+
+ /**
+ * The serializer to use when converting wrapped values to legacy strings.
+ * A null value indicates that we haven't created the serializer yet.
+ * This field should not be used directly, use {@link #getLegacySerializer()} instead.
+ */
+ @Nullable
+ private static LegacyComponentSerializer legacySerializer;
+
+ /**
+ * Wraps the specified Adventure component.
+ *
+ * @param value the value to wrap
+ * @return an instance that wraps the specified value
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static ComponentHolder of(@NotNull Component value) {
+ Validate.notNull(value, "value mustn't be null");
+ return isNativeAdventureSupport()
+ ? new NativeComponentHolder(value)
+ : new ForeignComponentHolder(value);
+ }
+
+ /**
+ * Gets whether the server platform natively supports Adventure.
+ * Native Adventure support means that eg. {@link ItemMeta#displayName(Component)}
+ * is a valid method.
+ *
+ * @return whether the server platform natively supports Adventure
+ * @since 0.10.0
+ */
+ private static boolean isNativeAdventureSupport() {
+ if (nativeAdventureSupport == null) {
+ try {
+ Component component = Component.text("test");
+ NativeComponentHolder holder = new NativeComponentHolder(component);
+
+ //If NoSuchMethodError or something is thrown we can assume that
+ //Adventure components are not natively supported by the server platform
+
+ //noinspection unused
+ Object ignored1 = holder.asInventoryTitle(null, 9);
+ //noinspection unused
+ Object ignored2 = holder.asInventoryTitle(null, InventoryType.HOPPER);
+
+ ItemMeta meta = new ItemStack(Material.STONE).getItemMeta();
+ holder.asItemDisplayName(meta);
+ holder.asItemLoreAtEnd(meta);
+
+ nativeAdventureSupport = true;
+ } catch (Throwable t) {
+ nativeAdventureSupport = false;
+ }
+ }
+ return nativeAdventureSupport;
+ }
+
+ /**
+ * Gets the serializer to use when converting wrapped values to legacy strings.
+ * Main use case being the implementation of {@link #asLegacyString()}.
+ *
+ * @return a serializer for converting wrapped values to legacy strings
+ * @since 0.10.0
+ */
+ private static LegacyComponentSerializer getLegacySerializer() {
+ if (legacySerializer == null) {
+ LegacyComponentSerializer.Builder builder = LegacyComponentSerializer.builder()
+ .character(LegacyComponentSerializer.SECTION_CHAR);
+ if (!net.md_5.bungee.api.ChatColor.class.isEnum()) {
+ //1.16+ Spigot (or Paper), hex colors are supported, no need to down sample them
+ builder.hexColors()
+ .useUnusualXRepeatedCharacterHexFormat();
+ }
+ legacySerializer = builder.build();
+ }
+ return legacySerializer;
+ }
+
+ /**
+ * The Adventure component this instance wraps.
+ */
+ @NotNull
+ protected final Component value;
+
+ /**
+ * Creates and initializes a new instance.
+ *
+ * @param value the Adventure component this instance should wrap
+ * @since 0.10.0
+ */
+ ComponentHolder(@NotNull Component value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the Adventure component this instance wraps.
+ *
+ * @return the contained Adventure component
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public Component getComponent() {
+ return value;
+ }
+
+ /**
+ * Gets the wrapped Adventure component in a JSON representation.
+ *
+ * @return the contained Adventure component as JSON
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public JsonElement asJson() {
+ return GsonComponentSerializer.gson().serializeToTree(value);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{" + value + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other != null && getClass() == other.getClass()
+ && Objects.equals(value, ((ComponentHolder) other).value);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public String asLegacyString() {
+ return getLegacySerializer().serialize(value);
+ }
+}
diff --git a/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ForeignComponentHolder.java b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ForeignComponentHolder.java
new file mode 100644
index 000000000..ddc7955e9
--- /dev/null
+++ b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/ForeignComponentHolder.java
@@ -0,0 +1,70 @@
+package com.github.stefvanschie.inventoryframework.adventuresupport;
+
+import net.kyori.adventure.text.Component;
+import org.bukkit.Bukkit;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryHolder;
+import org.bukkit.inventory.Merchant;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * A {@link ComponentHolder} implementation for platforms where Adventure isn't natively supported.
+ * Adventure components are converted to legacy Strings before passed to the Bukkit API.
+ *
+ * @see NativeComponentHolder
+ * @since 0.10.0
+ */
+class ForeignComponentHolder extends ComponentHolder {
+
+ /**
+ * A {@link StringHolder} wrapping {@link #asLegacyString()}.
+ * This class depends on {@link StringHolder} to reduce code duplication.
+ */
+ @NotNull
+ private final StringHolder legacy;
+
+ /**
+ * Creates and initializes a new instance.
+ *
+ * @param value the Adventure component this instance should wrap
+ * @since 0.10.0
+ */
+ ForeignComponentHolder(@NotNull Component value) {
+ super(value);
+ legacy = StringHolder.of(asLegacyString());
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory asInventoryTitle(InventoryHolder holder, InventoryType type) {
+ return legacy.asInventoryTitle(holder, type);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory asInventoryTitle(InventoryHolder holder, int size) {
+ return legacy.asInventoryTitle(holder, size);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Merchant asMerchantTitle() {
+ return legacy.asMerchantTitle();
+ }
+
+ @Override
+ public void asItemDisplayName(ItemMeta meta) {
+ legacy.asItemDisplayName(meta);
+ }
+
+ @Override
+ public void asItemLoreAtEnd(ItemMeta meta) {
+ legacy.asItemLoreAtEnd(meta);
+ }
+}
diff --git a/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/NativeComponentHolder.java b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/NativeComponentHolder.java
new file mode 100644
index 000000000..eaea94d11
--- /dev/null
+++ b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/NativeComponentHolder.java
@@ -0,0 +1,70 @@
+package com.github.stefvanschie.inventoryframework.adventuresupport;
+
+import net.kyori.adventure.text.Component;
+import org.bukkit.Bukkit;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryHolder;
+import org.bukkit.inventory.Merchant;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A {@link ComponentHolder} implementation for platforms where Adventure is natively supported.
+ * Adventure components are directly passed to the Bukkit (Paper) API.
+ *
+ * @see ForeignComponentHolder
+ * @since 0.10.0
+ */
+class NativeComponentHolder extends ComponentHolder {
+
+ /**
+ * Creates and initializes a new instance.
+ *
+ * @param value the Adventure component this instance should wrap
+ * @since 0.10.0
+ */
+ NativeComponentHolder(@NotNull Component value) {
+ super(value);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory asInventoryTitle(InventoryHolder holder, InventoryType type) {
+ return Bukkit.createInventory(holder, type, value);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory asInventoryTitle(InventoryHolder holder, int size) {
+ return Bukkit.createInventory(holder, size, value);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Merchant asMerchantTitle() {
+ return Bukkit.createMerchant(value);
+ }
+
+ @Override
+ public void asItemDisplayName(ItemMeta meta) {
+ meta.displayName(value);
+ }
+
+ @Override
+ public void asItemLoreAtEnd(ItemMeta meta) {
+ List lore = meta.hasLore()
+ ? Objects.requireNonNull(meta.lore())
+ : new ArrayList<>();
+ lore.add(value);
+ meta.lore(lore);
+ }
+}
diff --git a/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/StringHolder.java b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/StringHolder.java
new file mode 100644
index 000000000..cfbbb7aa6
--- /dev/null
+++ b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/StringHolder.java
@@ -0,0 +1,138 @@
+package com.github.stefvanschie.inventoryframework.adventuresupport;
+
+import org.apache.commons.lang.Validate;
+import org.bukkit.Bukkit;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryHolder;
+import org.bukkit.inventory.Merchant;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Wrapper of a legacy string value.
+ * {@link org.bukkit.ChatColor} based formatting is used.
+ *
+ * @since 0.10.0
+ */
+public final class StringHolder extends TextHolder {
+
+ /**
+ * Cached instance which wraps an empty {@link String}.
+ */
+ @NotNull
+ private static final StringHolder EMPTY = StringHolder.of("");
+
+ /**
+ * Wraps the specified legacy string.
+ *
+ * @param value the value to wrap
+ * @return an instance that wraps the specified value
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static StringHolder of(@NotNull String value) {
+ Validate.notNull(value, "value mustn't be null");
+ return new StringHolder(value);
+ }
+
+ /**
+ * Gets an instance that contains no characters.
+ *
+ * @return an instance without any characters
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static StringHolder empty() {
+ return EMPTY;
+ }
+
+ /**
+ * The legacy string this instance wraps.
+ */
+ @NotNull
+ private final String value;
+
+ /**
+ * Creates and initializes a new instance.
+ *
+ * @param value the legacy string this instance should wrap
+ * @since 0.10.0
+ */
+ private StringHolder(@NotNull String value) {
+ this.value = value;
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{" + value + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other != null && getClass() == other.getClass()
+ && Objects.equals(value, ((StringHolder) other).value);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public String asLegacyString() {
+ return value;
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory asInventoryTitle(InventoryHolder holder, InventoryType type) {
+ //noinspection deprecation
+ return Bukkit.createInventory(holder, type, value);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory asInventoryTitle(InventoryHolder holder, int size) {
+ //noinspection deprecation
+ return Bukkit.createInventory(holder, size, value);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Merchant asMerchantTitle() {
+ //noinspection deprecation
+ return Bukkit.createMerchant(value);
+ }
+
+ @Override
+ public void asItemDisplayName(ItemMeta meta) {
+ //noinspection deprecation
+ meta.setDisplayName(value);
+ }
+
+ @Override
+ public void asItemLoreAtEnd(ItemMeta meta) {
+ //noinspection deprecation
+ List lore = meta.hasLore()
+ ? Objects.requireNonNull(meta.getLore())
+ : new ArrayList<>();
+ lore.add(value);
+ //noinspection deprecation
+ meta.setLore(lore);
+ }
+}
diff --git a/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/TextHolder.java b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/TextHolder.java
new file mode 100644
index 000000000..dea4b8d72
--- /dev/null
+++ b/adventure-support/src/main/java/com/github/stefvanschie/inventoryframework/adventuresupport/TextHolder.java
@@ -0,0 +1,132 @@
+package com.github.stefvanschie.inventoryframework.adventuresupport;
+
+import net.kyori.adventure.text.Component;
+import org.bukkit.ChatColor;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryHolder;
+import org.bukkit.inventory.Merchant;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Immutable wrapper of a text-like value.
+ * Support for both Adventure and legacy strings is achieved through this class.
+ * To get an instance of this class please refer to either {@link StringHolder#of(String)}
+ * or {@link ComponentHolder#of(Component)}.
+ * Other methods like {@link #empty()} and {@link #deserialize(String)}
+ * also exist, but their use cases are very limited.
+ *
+ * @see StringHolder
+ * @see ComponentHolder
+ * @since 0.10.0
+ */
+public abstract class TextHolder {
+
+ /**
+ * Gets an instance that contains no characters and no formatting.
+ *
+ * @return an instance without any characters or formatting
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static TextHolder empty() {
+ return StringHolder.empty();
+ }
+
+ /**
+ * Deserializes the specified {@link String} as a {@link TextHolder}.
+ * This method is still WIP and may change drastically in the future:
+ *
+ * Are we going to use MiniMessage if it's present?
+ * Is MiniMessage going to be opt-in? If yes, how do we opt-in?
+ *
+ *
+ * @param string the raw data to deserialize
+ * @return an instance containing the text from the string
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static TextHolder deserialize(@NotNull String string) {
+ return StringHolder.of(ChatColor.translateAlternateColorCodes('&', string));
+ }
+
+ TextHolder() {
+ //package-private constructor to "seal" the class
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public abstract String toString();
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object other);
+
+ /**
+ * Converts the text wrapped by this class instance to a legacy string,
+ * keeping the original formatting.
+ *
+ * @return the wrapped value represented as a legacy string
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public abstract String asLegacyString();
+
+ /**
+ * Creates a new inventory with the wrapped value as the inventory's title.
+ *
+ * @param holder the holder to use for the new inventory
+ * @param type the type of inventory to create
+ * @return a newly created inventory with the wrapped value as its title
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public abstract Inventory asInventoryTitle(InventoryHolder holder, InventoryType type);
+
+ /**
+ * Creates a new inventory with the wrapped value as the inventory's title.
+ *
+ * @param holder the holder to use for the new inventory
+ * @param size the count of slots the inventory should have (normal size restrictions apply)
+ * @return a newly created inventory with the wrapped value as its title
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public abstract Inventory asInventoryTitle(InventoryHolder holder, int size);
+
+ /**
+ * Creates a new merchant with the wrapped value as the merchant's title.
+ *
+ * @return a newly created inventory with the wrapped value as its title
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public abstract Merchant asMerchantTitle();
+
+ /**
+ * Modifies the specified meta: sets the display name to the wrapped value.
+ *
+ * @param meta the meta whose display name to set
+ * @since 0.10.0
+ */
+ public abstract void asItemDisplayName(ItemMeta meta);
+
+ /**
+ * Modifies the specified meta: adds the wrapped value as a new lore line at the end
+ *
+ * @param meta the meta whose lore to append to
+ * @since 0.10.0
+ */
+ public abstract void asItemLoreAtEnd(ItemMeta meta);
+}
diff --git a/nms/1_14_R1/pom.xml b/inventory-view/iv-abstract-class/pom.xml
similarity index 56%
rename from nms/1_14_R1/pom.xml
rename to inventory-view/iv-abstract-class/pom.xml
index 6dbcde799..c805f10fb 100644
--- a/nms/1_14_R1/pom.xml
+++ b/inventory-view/iv-abstract-class/pom.xml
@@ -2,32 +2,46 @@
+ 4.0.0
- IF-parent
com.github.stefvanschie.inventoryframework
- 0.9.8
+ IF-parent
+ 0.12.0
../../pom.xml
- 4.0.0
- 1_14_R1
+ iv-abstract-class
true
+
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
+
+
-
- org.spigotmc
- spigot
- 1.14.4-R0.1-SNAPSHOT
- provided
-
com.github.stefvanschie.inventoryframework
- abstraction
+ iv-abstraction
${project.version}
- compile
+
+
+ org.spigotmc
+ spigot-api
+ 1.20.6-R0.1-SNAPSHOT
+ provided
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
\ No newline at end of file
diff --git a/inventory-view/iv-abstract-class/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstractclass/InventoryViewUtil.java b/inventory-view/iv-abstract-class/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstractclass/InventoryViewUtil.java
new file mode 100644
index 000000000..eb8ec2084
--- /dev/null
+++ b/inventory-view/iv-abstract-class/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstractclass/InventoryViewUtil.java
@@ -0,0 +1,77 @@
+package com.github.stefvanschie.inventoryframework.inventoryview.abstractclass;
+
+import com.github.stefvanschie.inventoryframework.inventoryview.abstraction.AbstractInventoryViewUtil;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryView;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A wrapper for {@link InventoryView} methods that apply when {@link InventoryView} was an abstract class.
+ *
+ * @since 0.10.16
+ */
+public class InventoryViewUtil implements AbstractInventoryViewUtil {
+
+ /**
+ * Instance of this singleton class.
+ */
+ @NotNull
+ private static final InventoryViewUtil INSTANCE = new InventoryViewUtil();
+
+ @NotNull
+ @Override
+ public Inventory getBottomInventory(@NotNull InventoryView view) {
+ return view.getBottomInventory();
+ }
+
+ @Nullable
+ @Override
+ public ItemStack getCursor(@NotNull InventoryView view) {
+ return view.getCursor();
+ }
+
+ @Override
+ public void setCursor(@NotNull InventoryView view, @Nullable ItemStack item) {
+ view.setCursor(item);
+ }
+
+ @Nullable
+ @Override
+ public Inventory getInventory(@NotNull InventoryView view, int slot) {
+ return view.getInventory(slot);
+ }
+
+ @NotNull
+ @Override
+ public InventoryType.SlotType getSlotType(@NotNull InventoryView view, int slot) {
+ return view.getSlotType(slot);
+ }
+
+ @NotNull
+ @Override
+ public String getTitle(@NotNull InventoryView view) {
+ return view.getTitle();
+ }
+
+ @NotNull
+ @Override
+ public Inventory getTopInventory(@NotNull InventoryView view) {
+ return view.getTopInventory();
+ }
+
+ /**
+ * Gets the singleton instance of this class.
+ *
+ * @return the instance of this class
+ * @since 0.10.16
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static InventoryViewUtil getInstance() {
+ return INSTANCE;
+ }
+}
diff --git a/nms/1_16_R2/pom.xml b/inventory-view/iv-abstraction/pom.xml
similarity index 53%
rename from nms/1_16_R2/pom.xml
rename to inventory-view/iv-abstraction/pom.xml
index f630a7f1e..8fc2b6774 100644
--- a/nms/1_16_R2/pom.xml
+++ b/inventory-view/iv-abstraction/pom.xml
@@ -2,32 +2,41 @@
+ 4.0.0
- IF-parent
com.github.stefvanschie.inventoryframework
- 0.9.8
+ IF-parent
+ 0.12.0
../../pom.xml
- 4.0.0
- 1_16_R2
+ iv-abstraction
true
+
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
+
+
org.spigotmc
- spigot
- 1.16.3-R0.1-SNAPSHOT
+ spigot-api
+ 1.21-R0.1-SNAPSHOT
provided
-
-
- com.github.stefvanschie.inventoryframework
- abstraction
- ${project.version}
- compile
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
\ No newline at end of file
diff --git a/inventory-view/iv-abstraction/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstraction/AbstractInventoryViewUtil.java b/inventory-view/iv-abstraction/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstraction/AbstractInventoryViewUtil.java
new file mode 100644
index 000000000..8d7f0c6cc
--- /dev/null
+++ b/inventory-view/iv-abstraction/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/abstraction/AbstractInventoryViewUtil.java
@@ -0,0 +1,87 @@
+package com.github.stefvanschie.inventoryframework.inventoryview.abstraction;
+
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryView;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A wrapper for {@link InventoryView} methods that apply when {@link InventoryView} was an abstract class.
+ *
+ * @since 0.10.16
+ */
+public interface AbstractInventoryViewUtil {
+
+ /**
+ * Behaves according to {@link InventoryView#getBottomInventory()}.
+ *
+ * @param view the {@link InventoryView} to invoke {@link InventoryView#getBottomInventory()} on
+ * @return the result of invoking {@link InventoryView#getBottomInventory()}
+ * @since 0.10.16
+ */
+ @NotNull
+ Inventory getBottomInventory(@NotNull InventoryView view);
+
+ /**
+ * Behaves according to {@link InventoryView#getCursor()}.
+ *
+ * @param view the {@link InventoryView} to invoke {@link InventoryView#getCursor()} on
+ * @return the result of invoking {@link InventoryView#getCursor()}
+ * @since 0.10.16
+ */
+ @Nullable
+ ItemStack getCursor(@NotNull InventoryView view);
+
+ /**
+ * Behaves according to {@link InventoryView#setCursor(ItemStack)}.
+ *
+ * @param view the {@link InventoryView} to invoke {@link InventoryView#setCursor(ItemStack)} on
+ * @param item the {@link ItemStack} to apply when invoking {@link InventoryView#setCursor(ItemStack)}
+ * @since 0.10.16
+ */
+ void setCursor(@NotNull InventoryView view, @Nullable ItemStack item);
+
+ /**
+ * Behaves according to {@link InventoryView#getInventory(int)}.
+ *
+ * @param view the {@link InventoryView} to invoke {@link InventoryView#getInventory(int)} on
+ * @param slot the slot to apply when invoking {@link InventoryView#getInventory(int)}
+ * @return the result of invoking {@link InventoryView#getInventory(int)}
+ * @since 0.10.16
+ */
+ @Nullable
+ Inventory getInventory(@NotNull InventoryView view, int slot);
+
+ /**
+ * Behaves according to {@link InventoryView#getSlotType(int)}.
+ *
+ * @param view the {@link InventoryView} to invoke {@link InventoryView#getSlotType(int)} on
+ * @param slot the slot to apply when invoking {@link InventoryView#getSlotType(int)}
+ * @return the result of invoking {@link InventoryView#getSlotType(int)}
+ * @since 0.10.16
+ */
+ @NotNull
+ InventoryType.SlotType getSlotType(@NotNull InventoryView view, int slot);
+
+ /**
+ * Behaves according to {@link InventoryView#getTitle()}.
+ *
+ * @param view the {@link InventoryView} to invoke {@link InventoryView#getTitle()} on
+ * @return the result of invoking {@link InventoryView#getTitle()}
+ * @since 0.10.16
+ */
+ @NotNull
+ String getTitle(@NotNull InventoryView view);
+
+ /**
+ * Behaves according to {@link InventoryView#getTopInventory()}.
+ *
+ * @param view the {@link InventoryView} to invoke {@link InventoryView#getTopInventory()} on
+ * @return the result of invoking {@link InventoryView#getTopInventory()}
+ * @since 0.10.16
+ */
+ @NotNull
+ Inventory getTopInventory(@NotNull InventoryView view);
+}
diff --git a/nms/1_16_R1/pom.xml b/inventory-view/iv-interface/pom.xml
similarity index 56%
rename from nms/1_16_R1/pom.xml
rename to inventory-view/iv-interface/pom.xml
index 47e02eab6..4e21e623f 100644
--- a/nms/1_16_R1/pom.xml
+++ b/inventory-view/iv-interface/pom.xml
@@ -2,32 +2,46 @@
+ 4.0.0
- IF-parent
com.github.stefvanschie.inventoryframework
- 0.9.8
+ IF-parent
+ 0.12.0
../../pom.xml
- 4.0.0
- 1_16_R1
+ iv-interface
true
+
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
+
+
-
- org.spigotmc
- spigot
- 1.16.1-R0.1-SNAPSHOT
- provided
-
com.github.stefvanschie.inventoryframework
- abstraction
+ iv-abstraction
${project.version}
- compile
+
+
+ org.spigotmc
+ spigot-api
+ 1.21-R0.1-SNAPSHOT
+ provided
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
\ No newline at end of file
diff --git a/inventory-view/iv-interface/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/interface_/InventoryViewUtil.java b/inventory-view/iv-interface/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/interface_/InventoryViewUtil.java
new file mode 100644
index 000000000..9d94062a0
--- /dev/null
+++ b/inventory-view/iv-interface/src/main/java/com/github/stefvanschie/inventoryframework/inventoryview/interface_/InventoryViewUtil.java
@@ -0,0 +1,77 @@
+package com.github.stefvanschie.inventoryframework.inventoryview.interface_;
+
+import com.github.stefvanschie.inventoryframework.inventoryview.abstraction.AbstractInventoryViewUtil;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryView;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A wrapper for {@link InventoryView} methods that apply when {@link InventoryView} was an abstract class.
+ *
+ * @since 0.10.16
+ */
+public class InventoryViewUtil implements AbstractInventoryViewUtil {
+
+ /**
+ * Instance of this singleton class.
+ */
+ @NotNull
+ private static final InventoryViewUtil INSTANCE = new InventoryViewUtil();
+
+ @NotNull
+ @Override
+ public Inventory getBottomInventory(@NotNull InventoryView view) {
+ return view.getBottomInventory();
+ }
+
+ @Nullable
+ @Override
+ public ItemStack getCursor(@NotNull InventoryView view) {
+ return view.getCursor();
+ }
+
+ @Override
+ public void setCursor(@NotNull InventoryView view, @Nullable ItemStack item) {
+ view.setCursor(item);
+ }
+
+ @Nullable
+ @Override
+ public Inventory getInventory(@NotNull InventoryView view, int slot) {
+ return view.getInventory(slot);
+ }
+
+ @NotNull
+ @Override
+ public InventoryType.SlotType getSlotType(@NotNull InventoryView view, int slot) {
+ return view.getSlotType(slot);
+ }
+
+ @NotNull
+ @Override
+ public String getTitle(@NotNull InventoryView view) {
+ return view.getTitle();
+ }
+
+ @NotNull
+ @Override
+ public Inventory getTopInventory(@NotNull InventoryView view) {
+ return view.getTopInventory();
+ }
+
+ /**
+ * Gets the singleton instance of this class.
+ *
+ * @return the instance of this class
+ * @since 0.10.16
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static InventoryViewUtil getInstance() {
+ return INSTANCE;
+ }
+}
diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/AnvilInventoryImpl.java b/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/AnvilInventoryImpl.java
deleted file mode 100644
index a17945e39..000000000
--- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/AnvilInventoryImpl.java
+++ /dev/null
@@ -1,304 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_14_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
-import net.minecraft.server.v1_14_R1.*;
-import org.bukkit.Location;
-import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryAnvil;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal anvil inventory for 1.14 R1
- *
- * @since 0.8.0
- */
-public class AnvilInventoryImpl extends AnvilInventory {
-
- public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for an anvil should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerAnvil;
-
- int id = containerAnvil.windowId;
- ChatMessage message = new ChatMessage(title);
-
- entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.ANVIL, message));
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.a,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem));
- }
-
- @Override
- public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem));
- }
-
- @Override
- public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- sendResultItem(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearResultItem(@NotNull Player player) {
- sendResultItem(player, ItemStack.a);
- }
-
- @Override
- public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) {
- setCursor(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Sets the cursor of the given player
- *
- * @param player the player to set the cursor
- * @param item the item to set the cursor to
- * @since 0.8.0
- */
- private void setCursor(@NotNull Player player, @NotNull ItemStack item) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item));
- }
-
- /**
- * Sends the result item to the specified player with the given item
- *
- * @param player the player to send the result item to
- * @param item the result item
- * @since 0.8.0
- */
- private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container anvil for responding to item renaming
- *
- * @since 0.8.0
- */
- private class ContainerAnvilImpl extends ContainerAnvil {
-
- /**
- * The player for whom this anvil container is
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container anvil
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the repair inventory field
- */
- @NotNull
- private final Field repairInventoryField;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- /**
- * Field for accessing the container access field
- */
- @NotNull
- private final Field containerAccessField;
-
- /**
- * Creates a new custom anvil container for the specified player
- *
- * @param entityPlayer the player for who this anvil container is
- * @since 0.8.0
- */
- public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory,
- ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0)));
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- repairInventoryField = ContainerAnvil.class.getDeclaredField("repairInventory");
- repairInventoryField.setAccessible(true);
-
- resultInventoryField = ContainerAnvil.class.getDeclaredField("resultInventory");
- resultInventoryField.setAccessible(true);
-
- containerAccessField = ContainerAnvil.class.getDeclaredField("containerAccess");
- containerAccessField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- getRepairInventory().setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getRepairInventory().setItem(1, CraftItemStack.asNMSCopy(items[1]));
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- Location location = getContainerAccess().getLocation();
- CraftInventory inventory = new CraftInventoryAnvil(location, getRepairInventory(), getResultInventory(),
- this) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Override
- public void a(@Nullable String name) {
- text = name == null ? "" : name;
-
- sendResultItem(player, getResultInventory().getItem(0));
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- @NotNull
- @Contract(pure = true)
- private IInventory getRepairInventory() {
- try {
- return (IInventory) repairInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- @NotNull
- @Contract(pure = true)
- private ContainerAccess getContainerAccess() {
- try {
- return (ContainerAccess) containerAccessField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/BeaconInventoryImpl.java b/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/BeaconInventoryImpl.java
deleted file mode 100644
index e6fb366da..000000000
--- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/BeaconInventoryImpl.java
+++ /dev/null
@@ -1,181 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_14_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
-import net.minecraft.server.v1_14_R1.*;
-import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryBeacon;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal beacon inventory for 1.14 R1
- *
- * @since 0.8.0
- */
-public class BeaconInventoryImpl extends BeaconInventory {
-
- public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item);
-
- entityPlayer.activeContainer = containerBeacon;
-
- int id = containerBeacon.windowId;
- ChatMessage message = new ChatMessage("Beacon");
-
- entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message));
-
- sendItem(player, item);
- }
-
- @Override
- public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- NonNullList items = NonNullList.a(
- ItemStack.a, //the first item doesn't count for some reason, so send a dummy item
- CraftItemStack.asNMSCopy(item)
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container beacon
- *
- * @since 0.8.0
- */
- private class ContainerBeaconImpl extends ContainerBeacon {
-
- /**
- * The player for this beacon container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container beacon
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the beacon field
- */
- @NotNull
- private final Field beaconField;
-
- public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.beaconField = ContainerBeacon.class.getDeclaredField("beacon");
- this.beaconField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- try {
- ItemStack itemStack = CraftItemStack.asNMSCopy(item);
-
- ((IInventory) beaconField.get(this)).setItem(0, itemStack);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- try {
- CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- }
-}
diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/CartographyTableInventoryImpl.java b/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/CartographyTableInventoryImpl.java
deleted file mode 100644
index 75766664d..000000000
--- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/CartographyTableInventoryImpl.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_14_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
-import net.minecraft.server.v1_14_R1.*;
-import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryCartography;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal cartography table inventory for 1.14 R1
- *
- * @since 0.8.0
- */
-public class CartographyTableInventoryImpl extends CartographyTableInventory {
-
- public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl(
- entityPlayer, items
- );
-
- entityPlayer.activeContainer = containerCartographyTable;
-
- int id = containerCartographyTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.a,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container cartography table
- *
- * @since 0.8.0
- */
- private class ContainerCartographyTableImpl extends ContainerCartography {
-
- /**
- * The player for this cartography table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container cartography table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- inventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- inventory.setItem(1, CraftItemStack.asNMSCopy(items[1]));
-
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- }
-}
diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/EnchantingTableInventoryImpl.java b/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/EnchantingTableInventoryImpl.java
deleted file mode 100644
index 0acb73900..000000000
--- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/EnchantingTableInventoryImpl.java
+++ /dev/null
@@ -1,194 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_14_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
-import net.minecraft.server.v1_14_R1.*;
-import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryEnchanting;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal enchanting table inventory for 1.14 R1
- *
- * @since 0.8.0
- */
-public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
-
- public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 2) {
- throw new IllegalArgumentException(
- "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerEnchantmentTable;
-
- int id = containerEnchantmentTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.a,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container enchanting table
- *
- * @since 0.8.0
- */
- private class ContainerEnchantingTableImpl extends ContainerEnchantTable {
-
- /**
- * The player for this enchanting table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container enchanting table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the enchant slots field
- */
- @NotNull
- private final Field enchantSlotsField;
-
- public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots");
- this.enchantSlotsField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- try {
- IInventory input = (IInventory) enchantSlotsField.get(this);
-
- input.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- input.setItem(1, CraftItemStack.asNMSCopy(items[1]));
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- try {
- CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- } catch (IllegalAccessException exception) {
- exception.printStackTrace();
- }
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- }
-}
diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/GrindstoneInventoryImpl.java b/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/GrindstoneInventoryImpl.java
deleted file mode 100644
index c93769a78..000000000
--- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/GrindstoneInventoryImpl.java
+++ /dev/null
@@ -1,227 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_14_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
-import net.minecraft.server.v1_14_R1.*;
-import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryGrindstone;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal grindstone inventory for 1.14 R1
- *
- * @since 0.8.0
- */
-public class GrindstoneInventoryImpl extends GrindstoneInventory {
-
- public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerGrindstone;
-
- int id = containerGrindstone.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.GRINDSTONE, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.a,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container grindstone
- *
- * @since 0.8.0
- */
- private class ContainerGrindstoneImpl extends ContainerGrindstone {
-
- /**
- * The player for this grindstone container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container grindstone
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the craft inventory field
- */
- @NotNull
- private final Field craftInventoryField;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.craftInventoryField = ContainerGrindstone.class.getDeclaredField("craftInventory");
- this.craftInventoryField.setAccessible(true);
-
- this.resultInventoryField = ContainerGrindstone.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- getCraftInventory().setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getCraftInventory().setItem(1, CraftItemStack.asNMSCopy(items[1]));
-
- getResultInventory().setItem(2, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryGrindstone(getCraftInventory(), getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- /**
- * Gets the craft inventory
- *
- * @return the craft inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private IInventory getCraftInventory() {
- try {
- return (IInventory) craftInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- /**
- * Gets the result inventory
- *
- * @return the result inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/StonecutterInventoryImpl.java b/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/StonecutterInventoryImpl.java
deleted file mode 100644
index a193a995d..000000000
--- a/nms/1_14_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_14_R1/StonecutterInventoryImpl.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_14_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
-import net.minecraft.server.v1_14_R1.*;
-import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryStonecutter;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal stonecutter inventory for 1.14 R1
- *
- * @since 0.8.0
- */
-public class StonecutterInventoryImpl extends StonecutterInventory {
-
- public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 2) {
- throw new IllegalArgumentException(
- "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerEnchantmentTable;
-
- int id = containerEnchantmentTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.a,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container enchanting table
- *
- * @since 0.8.0
- */
- private class ContainerStonecutterImpl extends ContainerStonecutter {
-
- /**
- * The player for this enchanting table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container enchanting table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- inventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- /**
- * Gets the result inventory
- *
- * @return the result inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- public IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/AnvilInventoryImpl.java b/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/AnvilInventoryImpl.java
deleted file mode 100644
index 2cbfc8c58..000000000
--- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/AnvilInventoryImpl.java
+++ /dev/null
@@ -1,304 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_15_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
-import net.minecraft.server.v1_15_R1.*;
-import org.bukkit.Location;
-import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryAnvil;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal anvil inventory for 1.15 R1
- *
- * @since 0.8.0
- */
-public class AnvilInventoryImpl extends AnvilInventory {
-
- public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for an anvil should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerAnvil;
-
- int id = containerAnvil.windowId;
- ChatMessage message = new ChatMessage(title);
-
- entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.ANVIL, message));
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.a,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem));
- }
-
- @Override
- public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem));
- }
-
- @Override
- public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- sendResultItem(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearResultItem(@NotNull Player player) {
- sendResultItem(player, ItemStack.a);
- }
-
- @Override
- public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) {
- setCursor(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Sets the cursor of the given player
- *
- * @param player the player to set the cursor
- * @param item the item to set the cursor to
- * @since 0.8.0
- */
- private void setCursor(@NotNull Player player, @NotNull ItemStack item) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item));
- }
-
- /**
- * Sends the result item to the specified player with the given item
- *
- * @param player the player to send the result item to
- * @param item the result item
- * @since 0.8.0
- */
- private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container anvil for responding to item renaming
- *
- * @since 0.8.0
- */
- private class ContainerAnvilImpl extends ContainerAnvil {
-
- /**
- * The player for whom this anvil container is
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container anvil
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the repair inventory field
- */
- @NotNull
- private final Field repairInventoryField;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- /**
- * Field for accessing the container access field
- */
- @NotNull
- private final Field containerAccessField;
-
- /**
- * Creates a new custom anvil container for the specified player
- *
- * @param entityPlayer the player for who this anvil container is
- * @since 0.8.0
- */
- public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory,
- ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0)));
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- repairInventoryField = ContainerAnvil.class.getDeclaredField("repairInventory");
- repairInventoryField.setAccessible(true);
-
- resultInventoryField = ContainerAnvil.class.getDeclaredField("resultInventory");
- resultInventoryField.setAccessible(true);
-
- containerAccessField = ContainerAnvil.class.getDeclaredField("containerAccess");
- containerAccessField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- getRepairInventory().setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getRepairInventory().setItem(1, CraftItemStack.asNMSCopy(items[1]));
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- Location location = getContainerAccess().getLocation();
- CraftInventory inventory = new CraftInventoryAnvil(location, getRepairInventory(), getResultInventory(),
- this) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Override
- public void a(@Nullable String name) {
- text = name == null ? "" : name;
-
- sendResultItem(player, getResultInventory().getItem(0));
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- @NotNull
- @Contract(pure = true)
- private IInventory getRepairInventory() {
- try {
- return (IInventory) repairInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- @NotNull
- @Contract(pure = true)
- private ContainerAccess getContainerAccess() {
- try {
- return (ContainerAccess) containerAccessField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/BeaconInventoryImpl.java b/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/BeaconInventoryImpl.java
deleted file mode 100644
index 38d7d21df..000000000
--- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/BeaconInventoryImpl.java
+++ /dev/null
@@ -1,181 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_15_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
-import net.minecraft.server.v1_15_R1.*;
-import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryBeacon;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal beacon inventory for 1.15 R1
- *
- * @since 0.8.0
- */
-public class BeaconInventoryImpl extends BeaconInventory {
-
- public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item);
-
- entityPlayer.activeContainer = containerBeacon;
-
- int id = containerBeacon.windowId;
- ChatMessage message = new ChatMessage("Beacon");
-
- entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message));
-
- sendItem(player, item);
- }
-
- @Override
- public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- NonNullList items = NonNullList.a(
- ItemStack.a, //the first item doesn't count for some reason, so send a dummy item
- CraftItemStack.asNMSCopy(item)
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container beacon
- *
- * @since 0.8.0
- */
- private class ContainerBeaconImpl extends ContainerBeacon {
-
- /**
- * The player for this beacon container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container beacon
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the beacon field
- */
- @NotNull
- private final Field beaconField;
-
- public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.beaconField = ContainerBeacon.class.getDeclaredField("beacon");
- this.beaconField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- try {
- ItemStack itemStack = CraftItemStack.asNMSCopy(item);
-
- ((IInventory) beaconField.get(this)).setItem(0, itemStack);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- try {
- CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- }
-}
diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/CartographyTableInventoryImpl.java b/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/CartographyTableInventoryImpl.java
deleted file mode 100644
index 9748a472a..000000000
--- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/CartographyTableInventoryImpl.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_15_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
-import net.minecraft.server.v1_15_R1.*;
-import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryCartography;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal cartography table inventory for 1.15 R1
- *
- * @since 0.8.0
- */
-public class CartographyTableInventoryImpl extends CartographyTableInventory {
-
- public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl(
- entityPlayer, items
- );
-
- entityPlayer.activeContainer = containerCartographyTable;
-
- int id = containerCartographyTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.a,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container cartography table
- *
- * @since 0.8.0
- */
- private class ContainerCartographyTableImpl extends ContainerCartography {
-
- /**
- * The player for this cartography table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container cartography table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- inventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- inventory.setItem(1, CraftItemStack.asNMSCopy(items[1]));
-
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- }
-}
diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/EnchantingTableInventoryImpl.java b/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/EnchantingTableInventoryImpl.java
deleted file mode 100644
index 42018e6c9..000000000
--- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/EnchantingTableInventoryImpl.java
+++ /dev/null
@@ -1,194 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_15_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
-import net.minecraft.server.v1_15_R1.*;
-import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryEnchanting;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal enchanting table inventory for 1.15 R1
- *
- * @since 0.8.0
- */
-public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
-
- public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 2) {
- throw new IllegalArgumentException(
- "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerEnchantmentTable;
-
- int id = containerEnchantmentTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.a,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container enchanting table
- *
- * @since 0.8.0
- */
- private class ContainerEnchantingTableImpl extends ContainerEnchantTable {
-
- /**
- * The player for this enchanting table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container enchanting table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the enchant slots field
- */
- @NotNull
- private final Field enchantSlotsField;
-
- public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots");
- this.enchantSlotsField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- try {
- IInventory input = (IInventory) enchantSlotsField.get(this);
-
- input.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- input.setItem(1, CraftItemStack.asNMSCopy(items[1]));
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- try {
- CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- } catch (IllegalAccessException exception) {
- exception.printStackTrace();
- }
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- }
-}
diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/GrindstoneInventoryImpl.java b/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/GrindstoneInventoryImpl.java
deleted file mode 100644
index d582f5fcc..000000000
--- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/GrindstoneInventoryImpl.java
+++ /dev/null
@@ -1,227 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_15_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
-import net.minecraft.server.v1_15_R1.*;
-import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryGrindstone;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal grindstone inventory for 1.15 R1
- *
- * @since 0.8.0
- */
-public class GrindstoneInventoryImpl extends GrindstoneInventory {
-
- public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerGrindstone;
-
- int id = containerGrindstone.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.GRINDSTONE, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.a,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container grindstone
- *
- * @since 0.8.0
- */
- private class ContainerGrindstoneImpl extends ContainerGrindstone {
-
- /**
- * The player for this grindstone container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container grindstone
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the craft inventory field
- */
- @NotNull
- private final Field craftInventoryField;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.craftInventoryField = ContainerGrindstone.class.getDeclaredField("craftInventory");
- this.craftInventoryField.setAccessible(true);
-
- this.resultInventoryField = ContainerGrindstone.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- getCraftInventory().setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getCraftInventory().setItem(1, CraftItemStack.asNMSCopy(items[1]));
-
- getResultInventory().setItem(2, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryGrindstone(getCraftInventory(), getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- /**
- * Gets the craft inventory
- *
- * @return the craft inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private IInventory getCraftInventory() {
- try {
- return (IInventory) craftInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- /**
- * Gets the result inventory
- *
- * @return the result inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/StonecutterInventoryImpl.java b/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/StonecutterInventoryImpl.java
deleted file mode 100644
index ded6bb0d1..000000000
--- a/nms/1_15_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_15_R1/StonecutterInventoryImpl.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_15_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
-import net.minecraft.server.v1_15_R1.*;
-import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryStonecutter;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal stonecutter inventory for 1.15 R1
- *
- * @since 0.8.0
- */
-public class StonecutterInventoryImpl extends StonecutterInventory {
-
- public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 2) {
- throw new IllegalArgumentException(
- "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerEnchantmentTable;
-
- int id = containerEnchantmentTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.a,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container enchanting table
- *
- * @since 0.8.0
- */
- private class ContainerStonecutterImpl extends ContainerStonecutter {
-
- /**
- * The player for this enchanting table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container enchanting table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- inventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- /**
- * Gets the result inventory
- *
- * @return the result inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- public IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_16_R3/pom.xml b/nms/1_16_5/pom.xml
similarity index 82%
rename from nms/1_16_R3/pom.xml
rename to nms/1_16_5/pom.xml
index 5ddb316fd..cdba60784 100644
--- a/nms/1_16_R3/pom.xml
+++ b/nms/1_16_5/pom.xml
@@ -5,30 +5,30 @@
IF-parent
com.github.stefvanschie.inventoryframework
- 0.9.8
+ 0.12.0
../../pom.xml
4.0.0
- 1_16_R3
+ 1_16_5
true
+
+ io.papermc
+ paper
+ 1.16.5-R0.1-SNAPSHOT
+ provided
+
com.github.stefvanschie.inventoryframework
abstraction
${project.version}
compile
-
- org.spigotmc
- spigot
- 1.16.4-R0.1-SNAPSHOT
- provided
-
\ No newline at end of file
diff --git a/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/AnvilInventoryImpl.java b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/AnvilInventoryImpl.java
new file mode 100644
index 000000000..bb1b28008
--- /dev/null
+++ b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/AnvilInventoryImpl.java
@@ -0,0 +1,296 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_16_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_16_5.util.TextHolderUtil;
+import net.minecraft.server.v1_16_R3.BlockPosition;
+import net.minecraft.server.v1_16_R3.Container;
+import net.minecraft.server.v1_16_R3.ContainerAccess;
+import net.minecraft.server.v1_16_R3.ContainerAnvil;
+import net.minecraft.server.v1_16_R3.ContainerProperty;
+import net.minecraft.server.v1_16_R3.EntityHuman;
+import net.minecraft.server.v1_16_R3.EntityPlayer;
+import net.minecraft.server.v1_16_R3.IChatBaseComponent;
+import net.minecraft.server.v1_16_R3.ICrafting;
+import net.minecraft.server.v1_16_R3.IInventory;
+import net.minecraft.server.v1_16_R3.ITileInventory;
+import net.minecraft.server.v1_16_R3.InventoryClickType;
+import net.minecraft.server.v1_16_R3.InventoryLargeChest;
+import net.minecraft.server.v1_16_R3.InventorySubcontainer;
+import net.minecraft.server.v1_16_R3.ItemStack;
+import net.minecraft.server.v1_16_R3.PlayerInventory;
+import net.minecraft.server.v1_16_R3.Slot;
+import net.minecraft.server.v1_16_R3.World;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+/**
+ * Internal anvil inventory for 1.16 R3
+ *
+ * @since 0.8.0
+ */
+public class AnvilInventoryImpl extends AnvilInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ InventorySubcontainer inputSlots = new InventorySubcontainer(2);
+ InventorySubcontainer resultSlot = new InventorySubcontainer(1);
+
+ return new CraftInventoryAnvil(null, inputSlots, resultSlot, null) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ANVIL;
+ }
+
+ @Override
+ public IInventory getInventory() {
+ return new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Container createMenu(
+ int containerId,
+ @Nullable PlayerInventory inventory,
+ @NotNull EntityHuman player
+ ) {
+ return new ContainerAnvilImpl(containerId, player, inputSlots, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public IChatBaseComponent getScoreboardDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends InventorySubcontainer implements ITileInventory {}
+
+ /**
+ * A custom container anvil for responding to item renaming
+ *
+ * @since 0.8.0
+ */
+ private class ContainerAnvilImpl extends ContainerAnvil {
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final InventorySubcontainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final InventorySubcontainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * The field containing the properties.
+ */
+ @NotNull
+ private final Field propertiesField;
+
+ /**
+ * The field containing the listeners.
+ */
+ @NotNull
+ private final Field listenersField;
+
+ /**
+ * Creates a new custom anvil container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerAnvilImpl(
+ int containerId,
+ @NotNull EntityHuman player,
+ @NotNull InventorySubcontainer inputSlots,
+ @NotNull InventorySubcontainer resultSlot
+ ) {
+ super(containerId, player.inventory, ContainerAccess.at(player.world, BlockPosition.ZERO));
+
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ this.checkReachable = false;
+ this.levelCost.set(AnvilInventoryImpl.super.cost);
+
+ InventoryLargeChest compoundContainer = new InventoryLargeChest(inputSlots, resultSlot);
+
+ updateSlot(0, compoundContainer);
+ updateSlot(1, compoundContainer);
+ updateSlot(2, compoundContainer);
+
+ try {
+ this.propertiesField = Container.class.getDeclaredField("d");
+ this.propertiesField.setAccessible(true);
+
+ this.listenersField = Container.class.getDeclaredField("listeners");
+ this.listenersField.setAccessible(true);
+ } catch (NoSuchFieldException exception) {
+ throw new IllegalStateException(exception);
+ }
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(
+ this.containerAccess.getLocation(),
+ this.inputSlots,
+ this.resultSlot,
+ this
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void c() {
+ if (super.levelCost.c()) {
+ broadcastFullState();
+ } else {
+ for (int index = 0; index < super.slots.size(); index++) {
+ if (!ItemStack.matches(super.items.get(index), super.slots.get(index).getItem())) {
+ broadcastFullState();
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void a(@Nullable String name) {
+ name = name == null ? "" : name;
+
+ /* Only update if the name is actually different. This may be called even if the name is not different,
+ particularly when putting an item in the first slot. */
+ if (!name.equals(AnvilInventoryImpl.super.observableText.get())) {
+ AnvilInventoryImpl.super.observableText.set(name);
+ }
+
+ //the client predicts the output result, so we broadcast the state again to override it
+ broadcastFullState();
+ }
+
+ @Override
+ public ItemStack a(int index, int dragData, @NotNull InventoryClickType clickType, @NotNull EntityHuman player) {
+ ItemStack item = super.a(index, dragData, clickType, player);
+
+ //client predicts first slot, so send data to override
+ broadcastFullState();
+
+ return item;
+ }
+
+ @Override
+ protected ItemStack a(@NotNull EntityHuman player, @NotNull ItemStack stack) {
+ return stack;
+ }
+
+ @Override
+ public void a(@NotNull IInventory container) {
+ c();
+ }
+
+ @Override
+ public void e() {}
+
+ @Override
+ public void b(@NotNull EntityHuman nmsPlayer) {}
+
+ @Override
+ protected void a(@NotNull EntityHuman player, @NotNull World world, @NotNull IInventory inventory) {}
+
+ /**
+ * Broadcasts the full menu state to the client.
+ *
+ * @since 0.11.0
+ */
+ private void broadcastFullState() {
+ List properties;
+ try {
+ //noinspection unchecked
+ properties = (List) this.propertiesField.get(this);
+ } catch (IllegalAccessException exception) {
+ throw new IllegalStateException(exception);
+ }
+
+ for (int index = 0; index < properties.size(); index++) {
+ ContainerProperty property = properties.get(index);
+
+ if (property.c()) {
+ try {
+ //noinspection unchecked
+ for (ICrafting listener : (List) this.listenersField.get(this)) {
+ listener.setContainerData(this, index, property.get());
+ }
+ } catch (IllegalAccessException exception) {
+ throw new IllegalStateException(exception);
+ }
+ }
+ }
+
+ if (super.player instanceof EntityPlayer) {
+ ((EntityPlayer) super.player).updateInventory(this);
+ }
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull IInventory container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.index, slot.e, slot.f);
+ newSlot.rawSlotIndex = slot.rawSlotIndex;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/BeaconInventoryImpl.java b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/BeaconInventoryImpl.java
new file mode 100644
index 000000000..103125c9e
--- /dev/null
+++ b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/BeaconInventoryImpl.java
@@ -0,0 +1,164 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_16_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
+import net.minecraft.server.v1_16_R3.BlockPosition;
+import net.minecraft.server.v1_16_R3.ChatComponentText;
+import net.minecraft.server.v1_16_R3.Container;
+import net.minecraft.server.v1_16_R3.ContainerAccess;
+import net.minecraft.server.v1_16_R3.ContainerBeacon;
+import net.minecraft.server.v1_16_R3.ContainerProperties;
+import net.minecraft.server.v1_16_R3.EntityHuman;
+import net.minecraft.server.v1_16_R3.IChatBaseComponent;
+import net.minecraft.server.v1_16_R3.IInventory;
+import net.minecraft.server.v1_16_R3.ITileInventory;
+import net.minecraft.server.v1_16_R3.InventorySubcontainer;
+import net.minecraft.server.v1_16_R3.PlayerInventory;
+import net.minecraft.server.v1_16_R3.Slot;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal beacon inventory for 1.16 R3
+ *
+ * @since 0.8.0
+ */
+public class BeaconInventoryImpl extends BeaconInventory {
+
+ @NotNull
+ @Override
+ public Inventory createInventory() {
+ IInventory container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Container createMenu(
+ int containerId,
+ @Nullable PlayerInventory inventory,
+ @NotNull EntityHuman player
+ ) {
+ return new ContainerBeaconImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public IChatBaseComponent getScoreboardDisplayName() {
+ return new ChatComponentText("Beacon");
+ }
+ };
+
+ container.setMaxStackSize(1); //client limitation
+
+ return new CraftInventoryBeacon(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.BEACON;
+ }
+
+ @Override
+ public IInventory getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends InventorySubcontainer implements ITileInventory {
+
+ /**
+ * Creates a new inventory view provider with one slot.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container beacon
+ *
+ * @since 0.8.0
+ */
+ private static class ContainerBeaconImpl extends ContainerBeacon {
+
+ /**
+ * The player viewing this menu.
+ */
+ @NotNull
+ private final EntityHuman player;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final InventorySubcontainer inputSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom beacon container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @since 0.11.0
+ */
+ public ContainerBeaconImpl(
+ int containerId,
+ @NotNull EntityHuman player,
+ @NotNull InventorySubcontainer inputSlot
+ ) {
+ super(containerId, player.inventory, new ContainerProperties(3),
+ ContainerAccess.at(player.world, BlockPosition.ZERO));
+
+ this.player = player;
+ this.inputSlot = inputSlot;
+
+ super.checkReachable = false;
+
+ Slot slot = super.slots.get(0);
+
+ Slot newSlot = new Slot(inputSlot, slot.index, slot.e, slot.f);
+ newSlot.rawSlotIndex = slot.rawSlotIndex;
+
+ super.slots.set(0, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryBeacon inventory = new CraftInventoryBeacon(this.inputSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void b(@Nullable EntityHuman player) {}
+
+ }
+}
diff --git a/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/CartographyTableInventoryImpl.java b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..0b03a6a3b
--- /dev/null
+++ b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/CartographyTableInventoryImpl.java
@@ -0,0 +1,186 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_16_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_16_5.util.TextHolderUtil;
+import net.minecraft.server.v1_16_R3.*;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal cartography table inventory for 1.16 R3
+ *
+ * @since 0.8.0
+ */
+public class CartographyTableInventoryImpl extends CartographyTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ InventorySubcontainer resultSlot = new InventorySubcontainer(1);
+
+ IInventory container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Container createMenu(
+ int containerId,
+ @Nullable PlayerInventory inventory,
+ @NotNull EntityHuman player
+ ) {
+ return new ContainerCartographyTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public IChatBaseComponent getScoreboardDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryCartography(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.CARTOGRAPHY;
+ }
+
+ @Override
+ public IInventory getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends InventorySubcontainer implements ITileInventory {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container cartography table
+ *
+ * @since 0.8.0
+ */
+ private static class ContainerCartographyTableImpl extends ContainerCartography {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final InventorySubcontainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final InventorySubcontainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom cartography table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerCartographyTableImpl(
+ int containerId,
+ @NotNull EntityHuman player,
+ @NotNull InventorySubcontainer inputSlots,
+ @NotNull InventorySubcontainer resultSlot
+ ) {
+ super(containerId, player.inventory, ContainerAccess.at(player.getWorld(), BlockPosition.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ InventoryLargeChest container = new InventoryLargeChest(inputSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryCartography inventory = new CraftInventoryCartography(this.inputSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void a(@Nullable IInventory container) {}
+
+ @Override
+ public void b(@Nullable EntityHuman player) {}
+
+ @Override
+ protected void a(@Nullable EntityHuman player, @Nullable World world, @Nullable IInventory container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull IInventory container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.index, slot.e, slot.f);
+ newSlot.rawSlotIndex = slot.rawSlotIndex;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+
+ }
+}
diff --git a/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/EnchantingTableInventoryImpl.java b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..e7452058b
--- /dev/null
+++ b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/EnchantingTableInventoryImpl.java
@@ -0,0 +1,171 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_16_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_16_5.util.TextHolderUtil;
+import net.minecraft.server.v1_16_R3.*;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal enchanting table inventory for 1.16 R3
+ *
+ * @since 0.8.0
+ */
+public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ IInventory container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Container createMenu(
+ int containerId,
+ @Nullable PlayerInventory inventory,
+ @NotNull EntityHuman player
+ ) {
+ return new ContainerEnchantingTableImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public IChatBaseComponent getScoreboardDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryEnchanting(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ENCHANTING;
+ }
+
+ @Override
+ public IInventory getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends InventorySubcontainer implements ITileInventory {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.8.0
+ */
+ private static class ContainerEnchantingTableImpl extends ContainerEnchantTable {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final InventorySubcontainer inputSlots;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom enchanting table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @since 0.11.0
+ */
+ public ContainerEnchantingTableImpl(
+ int containerId,
+ @NotNull EntityHuman player,
+ @NotNull InventorySubcontainer inputSlots
+ ) {
+ super(containerId, player.inventory, ContainerAccess.at(player.getWorld(), BlockPosition.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+
+ super.checkReachable = false;
+
+ updateSlot(0, inputSlots);
+ updateSlot(1, inputSlots);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.inputSlots);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void a(@Nullable IInventory container) {}
+
+ @Override
+ public void b(@Nullable EntityHuman player) {}
+
+ @Override
+ protected void a(@Nullable EntityHuman player, @Nullable World world, @Nullable IInventory container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull IInventory container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.index, slot.e, slot.f);
+ newSlot.rawSlotIndex = slot.rawSlotIndex;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/GrindstoneInventoryImpl.java b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..c26cd3546
--- /dev/null
+++ b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/GrindstoneInventoryImpl.java
@@ -0,0 +1,185 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_16_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_16_5.util.TextHolderUtil;
+import net.minecraft.server.v1_16_R3.*;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal grindstone inventory for 1.16 R3
+ *
+ * @since 0.8.0
+ */
+public class GrindstoneInventoryImpl extends GrindstoneInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ InventorySubcontainer resultSlot = new InventorySubcontainer(1);
+
+ IInventory container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Container createMenu(
+ int containerId,
+ @Nullable PlayerInventory inventory,
+ @NotNull EntityHuman player
+ ) {
+ return new ContainerGrindstoneImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public IChatBaseComponent getScoreboardDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryGrindstone(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.GRINDSTONE;
+ }
+
+ @Override
+ public IInventory getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends InventorySubcontainer implements ITileInventory {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container grindstone
+ *
+ * @since 0.8.0
+ */
+ private static class ContainerGrindstoneImpl extends ContainerGrindstone {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final InventorySubcontainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final InventorySubcontainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerGrindstoneImpl(
+ int containerId,
+ @NotNull EntityHuman player,
+ @NotNull InventorySubcontainer itemsSlots,
+ @NotNull InventorySubcontainer resultSlot
+ ) {
+ super(containerId, player.inventory, ContainerAccess.at(player.getWorld(), BlockPosition.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ InventoryLargeChest container = new InventoryLargeChest(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryGrindstone inventory = new CraftInventoryGrindstone(this.itemsSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void a(@Nullable IInventory container) {}
+
+ @Override
+ public void b(@Nullable EntityHuman player) {}
+
+ @Override
+ protected void a(@Nullable EntityHuman player, @Nullable World world, @Nullable IInventory container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull IInventory container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.index, slot.e, slot.f);
+ newSlot.rawSlotIndex = slot.rawSlotIndex;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/MerchantInventoryImpl.java b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/MerchantInventoryImpl.java
new file mode 100644
index 000000000..4486f2073
--- /dev/null
+++ b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/MerchantInventoryImpl.java
@@ -0,0 +1,301 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_16_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_16_5.util.TextHolderUtil;
+import net.minecraft.server.v1_16_R3.Container;
+import net.minecraft.server.v1_16_R3.ContainerMerchant;
+import net.minecraft.server.v1_16_R3.EntityHuman;
+import net.minecraft.server.v1_16_R3.EntityPlayer;
+import net.minecraft.server.v1_16_R3.IChatBaseComponent;
+import net.minecraft.server.v1_16_R3.IInventory;
+import net.minecraft.server.v1_16_R3.IMerchant;
+import net.minecraft.server.v1_16_R3.ITileInventory;
+import net.minecraft.server.v1_16_R3.InventoryMerchant;
+import net.minecraft.server.v1_16_R3.MerchantRecipeList;
+import net.minecraft.server.v1_16_R3.MerchantWrapper;
+import net.minecraft.server.v1_16_R3.PlayerInventory;
+import net.minecraft.server.v1_16_R3.Slot;
+import net.minecraft.server.v1_16_R3.World;
+import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MerchantRecipe;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal merchant inventory for 1.16.4 - 1.16.5
+ *
+ * @since 0.10.1
+ */
+public class MerchantInventoryImpl extends MerchantInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ IMerchant merchant = new MerchantWrapper(null);
+
+ InventoryMerchant container = new InventoryViewProvider(merchant) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Container createMenu(
+ int containerId,
+ @Nullable PlayerInventory inventory,
+ @NotNull EntityHuman player
+ ) {
+ return new ContainerMerchantImpl(containerId, player, this, merchant);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public IChatBaseComponent getScoreboardDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryMerchant(merchant, container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.MERCHANT;
+ }
+
+ @Override
+ public InventoryMerchant getInventory() {
+ return container;
+ }
+ };
+ }
+
+ @Override
+ public void sendMerchantOffers(@NotNull Player player,
+ @NotNull List extends Map.Entry extends MerchantRecipe, ? extends Integer>> trades,
+ int level, int experience) {
+ MerchantRecipeList offers = new MerchantRecipeList();
+
+ for (Map.Entry extends MerchantRecipe, ? extends Integer> entry : trades) {
+ MerchantRecipe recipe = entry.getKey();
+ List ingredients = recipe.getIngredients();
+
+ if (ingredients.size() < 1) {
+ throw new IllegalStateException("Merchant recipe has no ingredients");
+ }
+
+ ItemStack itemA = ingredients.get(0);
+ ItemStack itemB = null;
+
+ if (ingredients.size() >= 2) {
+ itemB = ingredients.get(1);
+ }
+
+ net.minecraft.server.v1_16_R3.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA);
+ net.minecraft.server.v1_16_R3.ItemStack nmsItemB = net.minecraft.server.v1_16_R3.ItemStack.b;
+ net.minecraft.server.v1_16_R3.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult());
+
+ if (itemB != null) {
+ nmsItemB = CraftItemStack.asNMSCopy(itemB);
+ }
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ net.minecraft.server.v1_16_R3.MerchantRecipe merchantOffer = new net.minecraft.server.v1_16_R3.MerchantRecipe(
+ nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier
+ );
+ merchantOffer.setSpecialPrice(entry.getValue());
+
+ offers.add(merchantOffer);
+ }
+
+ EntityPlayer entityPlayer = getEntityPlayer(player);
+
+ entityPlayer.openTrade(getWindowId(entityPlayer), offers, level, experience, true, false);
+ }
+
+ /**
+ * Gets the entity player associated to this player
+ *
+ * @param player the player to get the entity player from
+ * @return the entity player
+ * @since 0.10.1
+ */
+ @NotNull
+ @Contract(pure = true)
+ private EntityPlayer getEntityPlayer(@NotNull Player player) {
+ return ((CraftPlayer) player).getHandle();
+ }
+
+ /**
+ * Gets the window id for the inventory view the player currently has open
+ *
+ * @param entityPlayer the player to get the window id for
+ * @return the window id
+ * @since 0.10.1
+ */
+ @Contract(pure = true)
+ private int getWindowId(@NotNull EntityPlayer entityPlayer) {
+ return entityPlayer.activeContainer.windowId;
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends InventoryMerchant implements ITileInventory {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public InventoryViewProvider(@NotNull IMerchant merchant) {
+ super(merchant);
+ }
+ }
+
+ /**
+ * A custom container merchant
+ *
+ * @since 0.11.0
+ */
+ private static class ContainerMerchantImpl extends ContainerMerchant {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final InventoryMerchant container;
+
+ /**
+ * The merchant.
+ */
+ @NotNull
+ private final IMerchant merchant;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param container the items slots
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public ContainerMerchantImpl(
+ int containerId,
+ @NotNull EntityHuman player,
+ @NotNull InventoryMerchant container,
+ @NotNull IMerchant merchant
+ ) {
+ super(containerId, player.inventory, merchant);
+
+ this.humanEntity = player.getBukkitEntity();
+ this.container = container;
+ this.merchant = merchant;
+
+ super.checkReachable = false;
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+
+ Slot slot = super.slots.get(2);
+
+ Slot newSlot = new Slot(container, slot.index, slot.e, slot.f) {
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean isAllowed(@Nullable EntityHuman player) {
+ return false;
+ }
+
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean isAllowed(@Nullable net.minecraft.server.v1_16_R3.ItemStack itemStack) {
+ return false;
+ }
+ };
+ newSlot.rawSlotIndex = slot.rawSlotIndex;
+
+ super.slots.set(2, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryMerchant inventory = new CraftInventoryMerchant(this.merchant, this.container);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void a(@Nullable IInventory container) {}
+
+ @Override
+ public void b(@Nullable EntityHuman player) {}
+
+ @Override
+ protected void a(@Nullable EntityHuman player, @Nullable World world, @Nullable IInventory container) {}
+
+ @Override
+ public void d(int i) {}
+
+ @Override
+ public void g(int i) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull IInventory container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.index, slot.e, slot.f);
+ newSlot.rawSlotIndex = slot.rawSlotIndex;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/SmithingTableInventoryImpl.java b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..5fc9b1e6d
--- /dev/null
+++ b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/SmithingTableInventoryImpl.java
@@ -0,0 +1,206 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_16_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_16_5.util.TextHolderUtil;
+import net.minecraft.server.v1_16_R3.*;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal smithing table inventory for 1.16 R3
+ *
+ * @since 0.8.0
+ */
+public class SmithingTableInventoryImpl extends SmithingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ InventoryCraftResult resultSlot = new InventoryCraftResult();
+
+ IInventory container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Container createMenu(
+ int containerId,
+ @Nullable PlayerInventory inventory,
+ @NotNull EntityHuman player
+ ) {
+ return new ContainerSmithingTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public IChatBaseComponent getScoreboardDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventorySmithing(null, container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.SMITHING;
+ }
+
+ @Override
+ public IInventory getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends InventorySubcontainer implements ITileInventory {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container smithing table
+ *
+ * @since 0.8.0
+ */
+ private static class ContainerSmithingTableImpl extends ContainerSmithing {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final InventorySubcontainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final InventoryCraftResult resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom smithing table container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerSmithingTableImpl(
+ int containerId,
+ @NotNull EntityHuman player,
+ @NotNull InventorySubcontainer itemsSlots,
+ @NotNull InventoryCraftResult resultSlot
+ ) {
+ super(containerId, player.inventory, ContainerAccess.at(player.getWorld(), BlockPosition.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ InventoryLargeChest container = new InventoryLargeChest(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventory inventory = new CraftInventorySmithing(
+ super.containerAccess.getLocation(),
+ this.itemsSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean canUse(@Nullable EntityHuman nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void a(IInventory container) {}
+
+ @Override
+ public void b(EntityHuman nmsPlayer) {}
+
+ @Override
+ public void e() {}
+
+ @Override
+ protected ItemStack a(EntityHuman player, ItemStack stack) {
+ return stack;
+ }
+
+ @Override
+ protected boolean b(EntityHuman player, boolean present) {
+ return true;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull IInventory container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.index, slot.e, slot.f);
+ newSlot.rawSlotIndex = slot.rawSlotIndex;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/StonecutterInventoryImpl.java b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..56547dfaa
--- /dev/null
+++ b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/StonecutterInventoryImpl.java
@@ -0,0 +1,193 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_16_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_16_5.util.TextHolderUtil;
+import net.minecraft.server.v1_16_R3.*;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal stonecutter inventory for 1.16 R3
+ *
+ * @since 0.8.0
+ */
+public class StonecutterInventoryImpl extends StonecutterInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ InventorySubcontainer resultSlot = new InventorySubcontainer(1);
+
+ IInventory container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Container createMenu(
+ int containerId,
+ @Nullable PlayerInventory inventory,
+ @NotNull EntityHuman player
+ ) {
+ return new ContainerStonecutterImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public IChatBaseComponent getScoreboardDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryStonecutter(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.STONECUTTER;
+ }
+
+ @Override
+ public IInventory getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends InventorySubcontainer implements ITileInventory {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.8.0
+ */
+ private static class ContainerStonecutterImpl extends ContainerStonecutter {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final InventorySubcontainer inputSlot;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final InventorySubcontainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom stonecutter container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerStonecutterImpl(
+ int containerId,
+ @NotNull EntityHuman player,
+ @NotNull InventorySubcontainer inputSlot,
+ @NotNull InventorySubcontainer resultSlot
+ ) {
+ super(containerId, player.inventory, ContainerAccess.at(player.getWorld(), BlockPosition.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlot = inputSlot;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ InventoryLargeChest container = new InventoryLargeChest(inputSlot, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryStonecutter inventory = new CraftInventoryStonecutter(this.inputSlot, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean canUse(@Nullable EntityHuman nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void a(IInventory container) {}
+
+ @Override
+ public void b(EntityHuman nmsPlayer) {}
+
+ @Contract(value = "_, _ -> false", pure = true)
+ @Override
+ public boolean a(@Nullable EntityHuman player, int index) {
+ return false;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull IInventory container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.index, slot.e, slot.f);
+ newSlot.rawSlotIndex = slot.rawSlotIndex;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/util/TextHolderUtil.java b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/util/TextHolderUtil.java
new file mode 100644
index 000000000..bd1c3a999
--- /dev/null
+++ b/nms/1_16_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_5/util/TextHolderUtil.java
@@ -0,0 +1,66 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_16_5.util;
+
+import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import net.minecraft.server.v1_16_R3.ChatComponentText;
+import net.minecraft.server.v1_16_R3.IChatBaseComponent;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * A utility class for adding {@link TextHolder} support.
+ *
+ * @since 0.10.0
+ */
+public final class TextHolderUtil {
+
+ private TextHolderUtil() {
+ //private constructor to prevent construction
+ }
+
+ /**
+ * Converts the specified value to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static IChatBaseComponent toComponent(@NotNull TextHolder holder) {
+ if (holder instanceof StringHolder) {
+ return toComponent((StringHolder) holder);
+ } else {
+ return toComponent((ComponentHolder) holder);
+ }
+ }
+
+ /**
+ * Converts the specified legacy string holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static IChatBaseComponent toComponent(@NotNull StringHolder holder) {
+ return new ChatComponentText(holder.asLegacyString());
+ }
+
+ /**
+ * Converts the specified Adventure component holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static IChatBaseComponent toComponent(@NotNull ComponentHolder holder) {
+ return Objects.requireNonNull(IChatBaseComponent.ChatSerializer.a(holder.asJson()));
+ }
+}
diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/AnvilInventoryImpl.java b/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/AnvilInventoryImpl.java
deleted file mode 100644
index 5da3e3f29..000000000
--- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/AnvilInventoryImpl.java
+++ /dev/null
@@ -1,241 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
-import net.minecraft.server.v1_16_R1.*;
-import org.bukkit.Location;
-import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryAnvil;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Internal anvil inventory for 1.16 R1
- *
- * @since 0.8.0
- */
-public class AnvilInventoryImpl extends AnvilInventory {
-
- public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for an anvil should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerAnvil;
-
- int id = containerAnvil.windowId;
- ChatMessage message = new ChatMessage(title);
-
- entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.ANVIL, message));
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem));
- }
-
- @Override
- public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem));
- }
-
- @Override
- public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- sendResultItem(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearResultItem(@NotNull Player player) {
- sendResultItem(player, ItemStack.b);
- }
-
- @Override
- public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) {
- setCursor(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Sets the cursor of the given player
- *
- * @param player the player to set the cursor
- * @param item the item to set the cursor to
- * @since 0.8.0
- */
- private void setCursor(@NotNull Player player, @NotNull ItemStack item) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item));
- }
-
- /**
- * Sends the result item to the specified player with the given item
- *
- * @param player the player to send the result item to
- * @param item the result item
- * @since 0.8.0
- */
- private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container anvil for responding to item renaming
- *
- * @since 0.8.0
- */
- private class ContainerAnvilImpl extends ContainerAnvil {
-
- /**
- * The player for whom this anvil container is
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container anvil
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Creates a new custom anvil container for the specified player
- *
- * @param entityPlayer the player for who this anvil container is
- * @since 0.8.0
- */
- public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory,
- ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0)));
-
- this.player = entityPlayer.getBukkitEntity();
-
- repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1]));
- resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- Location location = containerAccess.getLocation();
- CraftInventory inventory = new CraftInventoryAnvil(location, repairInventory, resultInventory,
- this) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Override
- public void a(@Nullable String name) {
- text = name == null ? "" : name;
-
- sendResultItem(player, resultInventory.getItem(0));
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
- }
-}
diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/BeaconInventoryImpl.java b/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/BeaconInventoryImpl.java
deleted file mode 100644
index 3bfc1f68b..000000000
--- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/BeaconInventoryImpl.java
+++ /dev/null
@@ -1,181 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
-import net.minecraft.server.v1_16_R1.*;
-import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryBeacon;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal beacon inventory for 1.16 R1
- *
- * @since 0.8.0
- */
-public class BeaconInventoryImpl extends BeaconInventory {
-
- public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item);
-
- entityPlayer.activeContainer = containerBeacon;
-
- int id = containerBeacon.windowId;
- ChatMessage message = new ChatMessage("Beacon");
-
- entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message));
-
- sendItem(player, item);
- }
-
- @Override
- public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- NonNullList items = NonNullList.a(
- ItemStack.b, //the first item doesn't count for some reason, so send a dummy item
- CraftItemStack.asNMSCopy(item)
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container beacon
- *
- * @since 0.8.0
- */
- private class ContainerBeaconImpl extends ContainerBeacon {
-
- /**
- * The player for this beacon container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container beacon
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the beacon field
- */
- @NotNull
- private final Field beaconField;
-
- public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.beaconField = ContainerBeacon.class.getDeclaredField("beacon");
- this.beaconField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- try {
- ItemStack itemStack = CraftItemStack.asNMSCopy(item);
-
- ((IInventory) beaconField.get(this)).setItem(0, itemStack);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- try {
- CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- }
-}
diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/CartographyTableInventoryImpl.java b/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/CartographyTableInventoryImpl.java
deleted file mode 100644
index e4d3895a0..000000000
--- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/CartographyTableInventoryImpl.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
-import net.minecraft.server.v1_16_R1.*;
-import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryCartography;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal cartography table inventory for 1.16 R1
- *
- * @since 0.8.0
- */
-public class CartographyTableInventoryImpl extends CartographyTableInventory {
-
- public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl(
- entityPlayer, items
- );
-
- entityPlayer.activeContainer = containerCartographyTable;
-
- int id = containerCartographyTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container cartography table
- *
- * @since 0.8.0
- */
- private class ContainerCartographyTableImpl extends ContainerCartography {
-
- /**
- * The player for this cartography table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container cartography table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- inventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- inventory.setItem(1, CraftItemStack.asNMSCopy(items[1]));
-
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- }
-}
diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/EnchantingTableInventoryImpl.java b/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/EnchantingTableInventoryImpl.java
deleted file mode 100644
index 901cceb5e..000000000
--- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/EnchantingTableInventoryImpl.java
+++ /dev/null
@@ -1,194 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
-import net.minecraft.server.v1_16_R1.*;
-import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryEnchanting;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal enchanting table inventory for 1.16 R1
- *
- * @since 0.8.0
- */
-public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
-
- public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 2) {
- throw new IllegalArgumentException(
- "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerEnchantmentTable;
-
- int id = containerEnchantmentTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container enchanting table
- *
- * @since 0.8.0
- */
- private class ContainerEnchantingTableImpl extends ContainerEnchantTable {
-
- /**
- * The player for this enchanting table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container enchanting table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the enchant slots field
- */
- @NotNull
- private final Field enchantSlotsField;
-
- public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots");
- this.enchantSlotsField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- try {
- IInventory input = (IInventory) enchantSlotsField.get(this);
-
- input.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- input.setItem(1, CraftItemStack.asNMSCopy(items[1]));
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- try {
- CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- } catch (IllegalAccessException exception) {
- exception.printStackTrace();
- }
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- }
-}
diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/GrindstoneInventoryImpl.java b/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/GrindstoneInventoryImpl.java
deleted file mode 100644
index 291db39c1..000000000
--- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/GrindstoneInventoryImpl.java
+++ /dev/null
@@ -1,227 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
-import net.minecraft.server.v1_16_R1.*;
-import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryGrindstone;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal grindstone inventory for 1.16 R1
- *
- * @since 0.8.0
- */
-public class GrindstoneInventoryImpl extends GrindstoneInventory {
-
- public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerGrindstone;
-
- int id = containerGrindstone.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.GRINDSTONE, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container grindstone
- *
- * @since 0.8.0
- */
- private class ContainerGrindstoneImpl extends ContainerGrindstone {
-
- /**
- * The player for this grindstone container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container grindstone
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the craft inventory field
- */
- @NotNull
- private final Field craftInventoryField;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.craftInventoryField = ContainerGrindstone.class.getDeclaredField("craftInventory");
- this.craftInventoryField.setAccessible(true);
-
- this.resultInventoryField = ContainerGrindstone.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- getCraftInventory().setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getCraftInventory().setItem(1, CraftItemStack.asNMSCopy(items[1]));
-
- getResultInventory().setItem(2, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryGrindstone(getCraftInventory(), getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- /**
- * Gets the craft inventory
- *
- * @return the craft inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private IInventory getCraftInventory() {
- try {
- return (IInventory) craftInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- /**
- * Gets the result inventory
- *
- * @return the result inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/SmithingTableInventoryImpl.java b/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/SmithingTableInventoryImpl.java
deleted file mode 100644
index c9e30f97c..000000000
--- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/SmithingTableInventoryImpl.java
+++ /dev/null
@@ -1,226 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
-import net.minecraft.server.v1_16_R1.*;
-import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventorySmithing;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Internal smithing table inventory for 1.16 R1
- *
- * @since 0.8.0
- */
-public class SmithingTableInventoryImpl extends SmithingTableInventory {
-
- public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a stonecutter should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerSmithingTable;
-
- int id = containerSmithingTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.SMITHING, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem));
- }
-
- @Override
- public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem));
- }
-
- @Override
- public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- sendResultItem(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearResultItem(@NotNull Player player) {
- sendResultItem(player, ItemStack.b);
- }
-
- @Override
- public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) {
- setCursor(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Sets the cursor of the given player
- *
- * @param player the player to set the cursor
- * @param item the item to set the cursor to
- * @since 0.8.0
- */
- private void setCursor(@NotNull Player player, @NotNull ItemStack item) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item));
- }
-
- /**
- * Sends the result item to the specified player with the given item
- *
- * @param player the player to send the result item to
- * @param item the result item
- * @since 0.8.0
- */
- private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container smithing table
- *
- * @since 0.8.0
- */
- private class ContainerSmithingTableImpl extends ContainerSmithing {
-
- /**
- * The player for this smithing table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container smithing table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- public ContainerSmithingTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory,
- ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0)));
-
- this.player = entityPlayer.getBukkitEntity();
-
- repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1]));
- resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventorySmithing(repairInventory, resultInventory) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
- }
-}
diff --git a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/StonecutterInventoryImpl.java b/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/StonecutterInventoryImpl.java
deleted file mode 100644
index 8c76fb843..000000000
--- a/nms/1_16_R1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R1/StonecutterInventoryImpl.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R1;
-
-import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
-import net.minecraft.server.v1_16_R1.*;
-import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryStonecutter;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal stonecutter inventory for 1.16 R1
- *
- * @since 0.8.0
- */
-public class StonecutterInventoryImpl extends StonecutterInventory {
-
- public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 2) {
- throw new IllegalArgumentException(
- "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerEnchantmentTable;
-
- int id = containerEnchantmentTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container enchanting table
- *
- * @since 0.8.0
- */
- private class ContainerStonecutterImpl extends ContainerStonecutter {
-
- /**
- * The player for this enchanting table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container enchanting table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- inventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- /**
- * Gets the result inventory
- *
- * @return the result inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- public IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/AnvilInventoryImpl.java b/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/AnvilInventoryImpl.java
deleted file mode 100644
index b0fc4bd74..000000000
--- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/AnvilInventoryImpl.java
+++ /dev/null
@@ -1,241 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R2;
-
-import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
-import net.minecraft.server.v1_16_R2.*;
-import org.bukkit.Location;
-import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftInventoryAnvil;
-import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Internal anvil inventory for 1.16 R2
- *
- * @since 0.8.0
- */
-public class AnvilInventoryImpl extends AnvilInventory {
-
- public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for an anvil should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerAnvil;
-
- int id = containerAnvil.windowId;
- ChatMessage message = new ChatMessage(title);
-
- entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.ANVIL, message));
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem));
- }
-
- @Override
- public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem));
- }
-
- @Override
- public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- sendResultItem(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearResultItem(@NotNull Player player) {
- sendResultItem(player, ItemStack.b);
- }
-
- @Override
- public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) {
- setCursor(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Sets the cursor of the given player
- *
- * @param player the player to set the cursor
- * @param item the item to set the cursor to
- * @since 0.8.0
- */
- private void setCursor(@NotNull Player player, @NotNull ItemStack item) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item));
- }
-
- /**
- * Sends the result item to the specified player with the given item
- *
- * @param player the player to send the result item to
- * @param item the result item
- * @since 0.8.0
- */
- private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container anvil for responding to item renaming
- *
- * @since 0.8.0
- */
- private class ContainerAnvilImpl extends ContainerAnvil {
-
- /**
- * The player for whom this anvil container is
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container anvil
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Creates a new custom anvil container for the specified player
- *
- * @param entityPlayer the player for who this anvil container is
- * @since 0.8.0
- */
- public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory,
- ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0)));
-
- this.player = entityPlayer.getBukkitEntity();
-
- repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1]));
- resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- Location location = containerAccess.getLocation();
- CraftInventory inventory = new CraftInventoryAnvil(location, repairInventory, resultInventory,
- this) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Override
- public void a(@Nullable String name) {
- text = name == null ? "" : name;
-
- sendResultItem(player, resultInventory.getItem(0));
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
- }
-}
diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/BeaconInventoryImpl.java b/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/BeaconInventoryImpl.java
deleted file mode 100644
index 2a8d3cc64..000000000
--- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/BeaconInventoryImpl.java
+++ /dev/null
@@ -1,178 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R2;
-
-import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
-import net.minecraft.server.v1_16_R2.*;
-import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R2.inventory.*;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal beacon inventory for 1.16 R2
- *
- * @since 0.8.0
- */
-public class BeaconInventoryImpl extends BeaconInventory {
-
- public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item);
-
- entityPlayer.activeContainer = containerBeacon;
-
- int id = containerBeacon.windowId;
- ChatMessage message = new ChatMessage("Beacon");
-
- entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message));
-
- sendItem(player, item);
- }
-
- @Override
- public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- NonNullList items = NonNullList.a(
- ItemStack.b, //the first item doesn't count for some reason, so send a dummy item
- CraftItemStack.asNMSCopy(item)
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container beacon
- *
- * @since 0.8.0
- */
- private class ContainerBeaconImpl extends ContainerBeacon {
-
- /**
- * The player for this beacon container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container beacon
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the beacon field
- */
- @NotNull
- private final Field beaconField;
-
- public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.beaconField = ContainerBeacon.class.getDeclaredField("beacon");
- this.beaconField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- try {
- ItemStack itemStack = CraftItemStack.asNMSCopy(item);
-
- ((IInventory) beaconField.get(this)).setItem(0, itemStack);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- try {
- CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- }
-}
diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/CartographyTableInventoryImpl.java b/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/CartographyTableInventoryImpl.java
deleted file mode 100644
index b11ac0f23..000000000
--- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/CartographyTableInventoryImpl.java
+++ /dev/null
@@ -1,196 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R2;
-
-import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
-import net.minecraft.server.v1_16_R2.*;
-import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R2.inventory.*;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal cartography table inventory for 1.16 R2
- *
- * @since 0.8.0
- */
-public class CartographyTableInventoryImpl extends CartographyTableInventory {
-
- public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl(
- entityPlayer, items
- );
-
- entityPlayer.activeContainer = containerCartographyTable;
-
- int id = containerCartographyTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container cartography table
- *
- * @since 0.8.0
- */
- private class ContainerCartographyTableImpl extends ContainerCartography {
-
- /**
- * The player for this cartography table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container cartography table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- inventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- inventory.setItem(1, CraftItemStack.asNMSCopy(items[1]));
-
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- }
-}
diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/EnchantingTableInventoryImpl.java b/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/EnchantingTableInventoryImpl.java
deleted file mode 100644
index 89145689c..000000000
--- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/EnchantingTableInventoryImpl.java
+++ /dev/null
@@ -1,191 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R2;
-
-import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
-import net.minecraft.server.v1_16_R2.*;
-import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R2.inventory.*;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal enchanting table inventory for 1.16 R2
- *
- * @since 0.8.0
- */
-public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
-
- public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 2) {
- throw new IllegalArgumentException(
- "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerEnchantmentTable;
-
- int id = containerEnchantmentTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container enchanting table
- *
- * @since 0.8.0
- */
- private class ContainerEnchantingTableImpl extends ContainerEnchantTable {
-
- /**
- * The player for this enchanting table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container enchanting table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the enchant slots field
- */
- @NotNull
- private final Field enchantSlotsField;
-
- public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots");
- this.enchantSlotsField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- try {
- IInventory input = (IInventory) enchantSlotsField.get(this);
-
- input.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- input.setItem(1, CraftItemStack.asNMSCopy(items[1]));
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- try {
- CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- } catch (IllegalAccessException exception) {
- exception.printStackTrace();
- }
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- }
-}
diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/GrindstoneInventoryImpl.java b/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/GrindstoneInventoryImpl.java
deleted file mode 100644
index 48bf2f02e..000000000
--- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/GrindstoneInventoryImpl.java
+++ /dev/null
@@ -1,224 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R2;
-
-import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
-import net.minecraft.server.v1_16_R2.*;
-import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R2.inventory.*;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal grindstone inventory for 1.16 R2
- *
- * @since 0.8.0
- */
-public class GrindstoneInventoryImpl extends GrindstoneInventory {
-
- public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerGrindstone;
-
- int id = containerGrindstone.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.GRINDSTONE, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container grindstone
- *
- * @since 0.8.0
- */
- private class ContainerGrindstoneImpl extends ContainerGrindstone {
-
- /**
- * The player for this grindstone container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container grindstone
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the craft inventory field
- */
- @NotNull
- private final Field craftInventoryField;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.craftInventoryField = ContainerGrindstone.class.getDeclaredField("craftInventory");
- this.craftInventoryField.setAccessible(true);
-
- this.resultInventoryField = ContainerGrindstone.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- getCraftInventory().setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getCraftInventory().setItem(1, CraftItemStack.asNMSCopy(items[1]));
-
- getResultInventory().setItem(2, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryGrindstone(getCraftInventory(), getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- /**
- * Gets the craft inventory
- *
- * @return the craft inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private IInventory getCraftInventory() {
- try {
- return (IInventory) craftInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- /**
- * Gets the result inventory
- *
- * @return the result inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/SmithingTableInventoryImpl.java b/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/SmithingTableInventoryImpl.java
deleted file mode 100644
index 82779d966..000000000
--- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/SmithingTableInventoryImpl.java
+++ /dev/null
@@ -1,224 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R2;
-
-import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
-import net.minecraft.server.v1_16_R2.*;
-import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R2.inventory.*;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Internal smithing table inventory for 1.16 R2
- *
- * @since 0.8.0
- */
-public class SmithingTableInventoryImpl extends SmithingTableInventory {
-
- public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a stonecutter should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerSmithingTable;
-
- int id = containerSmithingTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.SMITHING, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem));
- }
-
- @Override
- public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem));
- }
-
- @Override
- public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- sendResultItem(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearResultItem(@NotNull Player player) {
- sendResultItem(player, ItemStack.b);
- }
-
- @Override
- public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) {
- setCursor(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Sets the cursor of the given player
- *
- * @param player the player to set the cursor
- * @param item the item to set the cursor to
- * @since 0.8.0
- */
- private void setCursor(@NotNull Player player, @NotNull ItemStack item) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item));
- }
-
- /**
- * Sends the result item to the specified player with the given item
- *
- * @param player the player to send the result item to
- * @param item the result item
- * @since 0.8.0
- */
- private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container smithing table
- *
- * @since 0.8.0
- */
- private class ContainerSmithingTableImpl extends ContainerSmithing {
-
- /**
- * The player for this smithing table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container smithing table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- public ContainerSmithingTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory,
- ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0)));
-
- this.player = entityPlayer.getBukkitEntity();
-
- repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1]));
- resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventorySmithing(containerAccess.getLocation(), repairInventory,
- resultInventory) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
- }
-}
diff --git a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/StonecutterInventoryImpl.java b/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/StonecutterInventoryImpl.java
deleted file mode 100644
index 83d8d88d4..000000000
--- a/nms/1_16_R2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R2/StonecutterInventoryImpl.java
+++ /dev/null
@@ -1,196 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R2;
-
-import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
-import net.minecraft.server.v1_16_R2.*;
-import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R2.inventory.*;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal stonecutter inventory for 1.16 R2
- *
- * @since 0.8.0
- */
-public class StonecutterInventoryImpl extends StonecutterInventory {
-
- public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 2) {
- throw new IllegalArgumentException(
- "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerEnchantmentTable;
-
- int id = containerEnchantmentTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container enchanting table
- *
- * @since 0.8.0
- */
- private class ContainerStonecutterImpl extends ContainerStonecutter {
-
- /**
- * The player for this enchanting table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container enchanting table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- inventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- /**
- * Gets the result inventory
- *
- * @return the result inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- public IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/AnvilInventoryImpl.java b/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/AnvilInventoryImpl.java
deleted file mode 100644
index 636d6ce51..000000000
--- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/AnvilInventoryImpl.java
+++ /dev/null
@@ -1,241 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R3;
-
-import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
-import net.minecraft.server.v1_16_R3.*;
-import org.bukkit.Location;
-import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryAnvil;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Internal anvil inventory for 1.16 R3
- *
- * @since 0.8.0
- */
-public class AnvilInventoryImpl extends AnvilInventory {
-
- public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for an anvil should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerAnvil;
-
- int id = containerAnvil.windowId;
- ChatMessage message = new ChatMessage(title);
-
- entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.ANVIL, message));
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem));
- }
-
- @Override
- public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem));
- }
-
- @Override
- public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- sendResultItem(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearResultItem(@NotNull Player player) {
- sendResultItem(player, ItemStack.b);
- }
-
- @Override
- public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) {
- setCursor(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Sets the cursor of the given player
- *
- * @param player the player to set the cursor
- * @param item the item to set the cursor to
- * @since 0.8.0
- */
- private void setCursor(@NotNull Player player, @NotNull ItemStack item) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item));
- }
-
- /**
- * Sends the result item to the specified player with the given item
- *
- * @param player the player to send the result item to
- * @param item the result item
- * @since 0.8.0
- */
- private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container anvil for responding to item renaming
- *
- * @since 0.8.0
- */
- private class ContainerAnvilImpl extends ContainerAnvil {
-
- /**
- * The player for whom this anvil container is
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container anvil
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Creates a new custom anvil container for the specified player
- *
- * @param entityPlayer the player for who this anvil container is
- * @since 0.8.0
- */
- public ContainerAnvilImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory,
- ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0)));
-
- this.player = entityPlayer.getBukkitEntity();
-
- repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1]));
- resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- Location location = containerAccess.getLocation();
- CraftInventory inventory = new CraftInventoryAnvil(location, repairInventory, resultInventory,
- this) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Override
- public void a(@Nullable String name) {
- text = name == null ? "" : name;
-
- sendResultItem(player, resultInventory.getItem(0));
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
- }
-}
diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/BeaconInventoryImpl.java b/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/BeaconInventoryImpl.java
deleted file mode 100644
index b1a433ab3..000000000
--- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/BeaconInventoryImpl.java
+++ /dev/null
@@ -1,181 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R3;
-
-import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
-import net.minecraft.server.v1_16_R3.*;
-import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryBeacon;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal beacon inventory for 1.16 R3
- *
- * @since 0.8.0
- */
-public class BeaconInventoryImpl extends BeaconInventory {
-
- public BeaconInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(entityPlayer, item);
-
- entityPlayer.activeContainer = containerBeacon;
-
- int id = containerBeacon.windowId;
- ChatMessage message = new ChatMessage("Beacon");
-
- entityPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.BEACON, message));
-
- sendItem(player, item);
- }
-
- @Override
- public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- NonNullList items = NonNullList.a(
- ItemStack.b, //the first item doesn't count for some reason, so send a dummy item
- CraftItemStack.asNMSCopy(item)
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), items));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container beacon
- *
- * @since 0.8.0
- */
- private class ContainerBeaconImpl extends ContainerBeacon {
-
- /**
- * The player for this beacon container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container beacon
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the beacon field
- */
- @NotNull
- private final Field beaconField;
-
- public ContainerBeaconImpl(@NotNull EntityPlayer entityPlayer, @Nullable org.bukkit.inventory.ItemStack item) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.beaconField = ContainerBeacon.class.getDeclaredField("beacon");
- this.beaconField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- try {
- ItemStack itemStack = CraftItemStack.asNMSCopy(item);
-
- ((IInventory) beaconField.get(this)).setItem(0, itemStack);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- try {
- CraftInventory inventory = new CraftInventoryBeacon((IInventory) beaconField.get(this)) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- }
-}
diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/CartographyTableInventoryImpl.java b/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/CartographyTableInventoryImpl.java
deleted file mode 100644
index e63aba2f3..000000000
--- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/CartographyTableInventoryImpl.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R3;
-
-import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
-import net.minecraft.server.v1_16_R3.*;
-import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryCartography;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal cartography table inventory for 1.16 R3
- *
- * @since 0.8.0
- */
-public class CartographyTableInventoryImpl extends CartographyTableInventory {
-
- public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a cartography table should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl(
- entityPlayer, items
- );
-
- entityPlayer.activeContainer = containerCartographyTable;
-
- int id = containerCartographyTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.CARTOGRAPHY_TABLE, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container cartography table
- *
- * @since 0.8.0
- */
- private class ContainerCartographyTableImpl extends ContainerCartography {
-
- /**
- * The player for this cartography table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container cartography table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerCartographyTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.resultInventoryField = ContainerCartography.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- inventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- inventory.setItem(1, CraftItemStack.asNMSCopy(items[1]));
-
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryCartography(super.inventory, getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- }
-}
diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/EnchantingTableInventoryImpl.java b/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/EnchantingTableInventoryImpl.java
deleted file mode 100644
index 7c3584325..000000000
--- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/EnchantingTableInventoryImpl.java
+++ /dev/null
@@ -1,194 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R3;
-
-import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
-import net.minecraft.server.v1_16_R3.*;
-import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryEnchanting;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal enchanting table inventory for 1.16 R3
- *
- * @since 0.8.0
- */
-public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
-
- public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 2) {
- throw new IllegalArgumentException(
- "The amount of items for an enchanting table should be 2, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerEnchantmentTable;
-
- int id = containerEnchantmentTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.ENCHANTMENT, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container enchanting table
- *
- * @since 0.8.0
- */
- private class ContainerEnchantingTableImpl extends ContainerEnchantTable {
-
- /**
- * The player for this enchanting table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container enchanting table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the enchant slots field
- */
- @NotNull
- private final Field enchantSlotsField;
-
- public ContainerEnchantingTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.enchantSlotsField = ContainerEnchantTable.class.getDeclaredField("enchantSlots");
- this.enchantSlotsField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- try {
- IInventory input = (IInventory) enchantSlotsField.get(this);
-
- input.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- input.setItem(1, CraftItemStack.asNMSCopy(items[1]));
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- try {
- CraftInventory inventory = new CraftInventoryEnchanting((IInventory) enchantSlotsField.get(this)) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- } catch (IllegalAccessException exception) {
- exception.printStackTrace();
- }
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- }
-}
diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/GrindstoneInventoryImpl.java b/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/GrindstoneInventoryImpl.java
deleted file mode 100644
index 140cf4d7b..000000000
--- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/GrindstoneInventoryImpl.java
+++ /dev/null
@@ -1,227 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R3;
-
-import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
-import net.minecraft.server.v1_16_R3.*;
-import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryGrindstone;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal grindstone inventory for 1.16 R3
- *
- * @since 0.8.0
- */
-public class GrindstoneInventoryImpl extends GrindstoneInventory {
-
- public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a grindstone should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerGrindstone;
-
- int id = containerGrindstone.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.GRINDSTONE, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container grindstone
- *
- * @since 0.8.0
- */
- private class ContainerGrindstoneImpl extends ContainerGrindstone {
-
- /**
- * The player for this grindstone container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container grindstone
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the craft inventory field
- */
- @NotNull
- private final Field craftInventoryField;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerGrindstoneImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.craftInventoryField = ContainerGrindstone.class.getDeclaredField("craftInventory");
- this.craftInventoryField.setAccessible(true);
-
- this.resultInventoryField = ContainerGrindstone.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- getCraftInventory().setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getCraftInventory().setItem(1, CraftItemStack.asNMSCopy(items[1]));
-
- getResultInventory().setItem(2, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryGrindstone(getCraftInventory(), getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- /**
- * Gets the craft inventory
- *
- * @return the craft inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private IInventory getCraftInventory() {
- try {
- return (IInventory) craftInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
-
- /**
- * Gets the result inventory
- *
- * @return the result inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/SmithingTableInventoryImpl.java b/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/SmithingTableInventoryImpl.java
deleted file mode 100644
index a9e9e5d60..000000000
--- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/SmithingTableInventoryImpl.java
+++ /dev/null
@@ -1,227 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R3;
-
-import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
-import net.minecraft.server.v1_16_R3.*;
-import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventorySmithing;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Internal smithing table inventory for 1.16 R3
- *
- * @since 0.8.0
- */
-public class SmithingTableInventoryImpl extends SmithingTableInventory {
-
- public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 3) {
- throw new IllegalArgumentException(
- "The amount of items for a smithing table should be 3, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerSmithingTable;
-
- int id = containerSmithingTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.SMITHING, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1]),
- CraftItemStack.asNMSCopy(items[2])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 0, nmsItem));
- }
-
- @Override
- public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 1, nmsItem));
- }
-
- @Override
- public void sendResultItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) {
- sendResultItem(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearResultItem(@NotNull Player player) {
- sendResultItem(player, ItemStack.b);
- }
-
- @Override
- public void setCursor(@NotNull Player player, @NotNull org.bukkit.inventory.ItemStack item) {
- setCursor(player, CraftItemStack.asNMSCopy(item));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Sets the cursor of the given player
- *
- * @param player the player to set the cursor
- * @param item the item to set the cursor to
- * @since 0.8.0
- */
- private void setCursor(@NotNull Player player, @NotNull ItemStack item) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, item));
- }
-
- /**
- * Sends the result item to the specified player with the given item
- *
- * @param player the player to send the result item to
- * @param item the result item
- * @since 0.8.0
- */
- private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) {
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutSetSlot(getWindowId(entityPlayer), 2, item));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container smithing table
- *
- * @since 0.8.0
- */
- private class ContainerSmithingTableImpl extends ContainerSmithing {
-
- /**
- * The player for this smithing table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container smithing table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- public ContainerSmithingTableImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory,
- ContainerAccess.at(entityPlayer.getWorld(), new BlockPosition(0, 0, 0)));
-
- this.player = entityPlayer.getBukkitEntity();
-
- repairInventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- repairInventory.setItem(1, CraftItemStack.asNMSCopy(items[1]));
- resultInventory.setItem(0, CraftItemStack.asNMSCopy(items[2]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventorySmithing(containerAccess.getLocation(), repairInventory,
- resultInventory) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
- }
-}
diff --git a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/StonecutterInventoryImpl.java b/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/StonecutterInventoryImpl.java
deleted file mode 100644
index 2e9db691e..000000000
--- a/nms/1_16_R3/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_16_R3/StonecutterInventoryImpl.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.github.stefvanschie.inventoryframework.nms.v1_16_R3;
-
-import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
-import net.minecraft.server.v1_16_R3.*;
-import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventory;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryStonecutter;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftInventoryView;
-import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
-import org.bukkit.entity.Player;
-import org.bukkit.inventory.InventoryHolder;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-
-/**
- * Internal stonecutter inventory for 1.16 R3
- *
- * @since 0.8.0
- */
-public class StonecutterInventoryImpl extends StonecutterInventory {
-
- public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) {
- super(inventoryHolder);
- }
-
- @Override
- public void openInventory(@NotNull Player player, @NotNull String title,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- int itemAmount = items.length;
-
- if (itemAmount != 2) {
- throw new IllegalArgumentException(
- "The amount of items for a stonecutter should be 2, but is '" + itemAmount + "'"
- );
- }
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
- ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(entityPlayer, items);
-
- entityPlayer.activeContainer = containerEnchantmentTable;
-
- int id = containerEnchantmentTable.windowId;
- ChatMessage message = new ChatMessage(title);
- PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, Containers.STONECUTTER, message);
-
- entityPlayer.playerConnection.sendPacket(packet);
-
- sendItems(player, items);
- }
-
- @Override
- public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) {
- NonNullList nmsItems = NonNullList.a(
- ItemStack.b,
- CraftItemStack.asNMSCopy(items[0]),
- CraftItemStack.asNMSCopy(items[1])
- );
-
- EntityPlayer entityPlayer = getEntityPlayer(player);
-
- getPlayerConnection(entityPlayer).sendPacket(new PacketPlayOutWindowItems(getWindowId(entityPlayer), nmsItems));
- }
-
- @Override
- public void clearCursor(@NotNull Player player) {
- getPlayerConnection(getEntityPlayer(player)).sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.b));
- }
-
- /**
- * Gets the window id for the inventory view the player currently has open
- *
- * @param entityPlayer the player to get the window id for
- * @return the window id
- * @since 0.8.0
- */
- @Contract(pure = true)
- private int getWindowId(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.activeContainer.windowId;
- }
-
- /**
- * Gets the player connection for the specified player
- *
- * @param entityPlayer the player to get the player connection from
- * @return the player connection
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private PlayerConnection getPlayerConnection(@NotNull EntityPlayer entityPlayer) {
- return entityPlayer.playerConnection;
- }
-
- /**
- * Gets the entity player associated to this player
- *
- * @param player the player to get the entity player from
- * @return the entity player
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- private EntityPlayer getEntityPlayer(@NotNull Player player) {
- return ((CraftPlayer) player).getHandle();
- }
-
- /**
- * A custom container enchanting table
- *
- * @since 0.8.0
- */
- private class ContainerStonecutterImpl extends ContainerStonecutter {
-
- /**
- * The player for this enchanting table container
- */
- @NotNull
- private final Player player;
-
- /**
- * The internal bukkit entity for this container enchanting table
- */
- @Nullable
- private CraftInventoryView bukkitEntity;
-
- /**
- * Field for accessing the result inventory field
- */
- @NotNull
- private final Field resultInventoryField;
-
- public ContainerStonecutterImpl(@NotNull EntityPlayer entityPlayer,
- @Nullable org.bukkit.inventory.ItemStack[] items) {
- super(entityPlayer.nextContainerCounter(), entityPlayer.inventory);
-
- this.player = entityPlayer.getBukkitEntity();
-
- try {
- this.resultInventoryField = ContainerStonecutter.class.getDeclaredField("resultInventory");
- this.resultInventoryField.setAccessible(true);
- } catch (NoSuchFieldException exception) {
- throw new RuntimeException(exception);
- }
-
- inventory.setItem(0, CraftItemStack.asNMSCopy(items[0]));
- getResultInventory().setItem(0, CraftItemStack.asNMSCopy(items[1]));
- }
-
- @NotNull
- @Override
- public CraftInventoryView getBukkitView() {
- if (bukkitEntity == null) {
- CraftInventory inventory = new CraftInventoryStonecutter(this.inventory, getResultInventory()) {
- @NotNull
- @Contract(pure = true)
- @Override
- public InventoryHolder getHolder() {
- return inventoryHolder;
- }
- };
-
- bukkitEntity = new CraftInventoryView(player, inventory, this);
- }
-
- return bukkitEntity;
- }
-
- @Contract(pure = true, value = "_ -> true")
- @Override
- public boolean canUse(@Nullable EntityHuman entityhuman) {
- return true;
- }
-
- @Override
- public void a(IInventory inventory) {}
-
- @Override
- public void b(EntityHuman entityhuman) {}
-
- /**
- * Gets the result inventory
- *
- * @return the result inventory
- * @since 0.8.0
- */
- @NotNull
- @Contract(pure = true)
- public IInventory getResultInventory() {
- try {
- return (IInventory) resultInventoryField.get(this);
- } catch (IllegalAccessException exception) {
- throw new RuntimeException(exception);
- }
- }
- }
-}
diff --git a/nms/1_17_1/pom.xml b/nms/1_17_1/pom.xml
new file mode 100644
index 000000000..4db65989a
--- /dev/null
+++ b/nms/1_17_1/pom.xml
@@ -0,0 +1,59 @@
+
+
+
+ IF-parent
+ com.github.stefvanschie.inventoryframework
+ 0.12.0
+ ../../pom.xml
+
+ 4.0.0
+
+ 1_17_1
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ ca.bkaw
+ paper-nms
+ 1.17.1-SNAPSHOT
+ provided
+
+
+
+
+
+
+ ca.bkaw
+ paper-nms-maven-plugin
+ 1.4.10
+
+
+ process-classes
+
+ remap
+
+
+
+
+
+
+
+
+
+ bytecode.space
+ https://repo.bytecode.space/repository/maven-public/
+
+
+
+
\ No newline at end of file
diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/AnvilInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/AnvilInventoryImpl.java
new file mode 100644
index 000000000..b514a51e9
--- /dev/null
+++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/AnvilInventoryImpl.java
@@ -0,0 +1,228 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_17_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.AnvilMenu;
+import net.minecraft.world.inventory.ClickType;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal anvil inventory for 1.17 R1
+ *
+ * @since 0.10.0
+ */
+public class AnvilInventoryImpl extends AnvilInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer inputSlots = new SimpleContainer(2);
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ return new CraftInventoryAnvil(null, inputSlots, resultSlot, null) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ANVIL;
+ }
+
+ @Override
+ public Container getInventory() {
+ return new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerAnvilImpl(containerId, player, inputSlots, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {}
+
+ /**
+ * A custom container anvil for responding to item renaming
+ *
+ * @since 0.10.0
+ */
+ private class ContainerAnvilImpl extends AnvilMenu {
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom anvil container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerAnvilImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ this.checkReachable = false;
+ this.cost.set(AnvilInventoryImpl.super.cost);
+
+ CompoundContainer compoundContainer = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, compoundContainer);
+ updateSlot(1, compoundContainer);
+ updateSlot(2, compoundContainer);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(
+ this.access.getLocation(),
+ this.inputSlots,
+ this.resultSlot,
+ this
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void broadcastChanges() {
+ if (super.cost.checkAndClearUpdateFlag()) {
+ broadcastFullState();
+ } else {
+ for (int index = 0; index < super.slots.size(); index++) {
+ if (!ItemStack.matches(super.remoteSlots.get(index), super.slots.get(index).getItem())) {
+ broadcastFullState();
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setItemName(@Nullable String name) {
+ name = name == null ? "" : name;
+
+ /* Only update if the name is actually different. This may be called even if the name is not different,
+ particularly when putting an item in the first slot. */
+ if (!name.equals(AnvilInventoryImpl.super.observableText.get())) {
+ AnvilInventoryImpl.super.observableText.set(name);
+ }
+
+ //the client predicts the output result, so we broadcast the state again to override it
+ broadcastFullState();
+ }
+
+ @Override
+ public void slotsChanged(@NotNull Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, @NotNull ClickType clickType, @NotNull Player player) {
+ super.clicked(index, dragData, clickType, player);
+
+ //client predicts first slot, so send data to override
+ broadcastFullState();
+ }
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ public void removed(@NotNull Player nmsPlayer) {}
+
+ @Override
+ protected void clearContainer(@NotNull Player player, @NotNull Container inventory) {}
+
+ @Override
+ protected void onTake(@NotNull Player player, @NotNull ItemStack stack) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/BeaconInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/BeaconInventoryImpl.java
new file mode 100644
index 000000000..4ea869e26
--- /dev/null
+++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/BeaconInventoryImpl.java
@@ -0,0 +1,162 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_17_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.TextComponent;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.BeaconMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.SimpleContainerData;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal beacon inventory for 1.17 R1
+ *
+ * @since 0.10.0
+ */
+public class BeaconInventoryImpl extends BeaconInventory {
+
+ @NotNull
+ @Override
+ public Inventory createInventory() {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerBeaconImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return new TextComponent("Beacon");
+ }
+ };
+
+ container.setMaxStackSize(1); //client limitation
+
+ return new CraftInventoryBeacon(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.BEACON;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with one slot.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container beacon
+ *
+ * @since 0.10.0
+ */
+ private static class ContainerBeaconImpl extends BeaconMenu {
+
+ /**
+ * The player viewing this menu.
+ */
+ @NotNull
+ private final Player player;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom beacon container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @since 0.11.0
+ */
+ public ContainerBeaconImpl(int containerId, @NotNull Player player, @NotNull SimpleContainer inputSlot) {
+ super(containerId, player.getInventory(), new SimpleContainerData(3),
+ ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.player = player;
+ this.inputSlot = inputSlot;
+
+ super.checkReachable = false;
+
+ Slot slot = super.slots.get(0);
+
+ Slot newSlot = new Slot(inputSlot, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(0, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryBeacon inventory = new CraftInventoryBeacon(this.inputSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ }
+}
diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/CartographyTableInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..2c4f73965
--- /dev/null
+++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/CartographyTableInventoryImpl.java
@@ -0,0 +1,196 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_17_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.CartographyTableMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal cartography table inventory for 1.17 R1
+ *
+ * @since 0.10.0
+ */
+public class CartographyTableInventoryImpl extends CartographyTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerCartographyTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryCartography(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.CARTOGRAPHY;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container cartography table
+ *
+ * @since 0.10.0
+ */
+ private static class ContainerCartographyTableImpl extends CartographyTableMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom cartography table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerCartographyTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryCartography inventory = new CraftInventoryCartography(this.inputSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+
+ }
+}
diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/EnchantingTableInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..10fa539c4
--- /dev/null
+++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/EnchantingTableInventoryImpl.java
@@ -0,0 +1,180 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_17_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.EnchantmentMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal enchanting table inventory for 1.17 R1
+ *
+ * @since 0.10.0
+ */
+public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerEnchantingTableImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryEnchanting(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ENCHANTING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.0
+ */
+ private static class ContainerEnchantingTableImpl extends EnchantmentMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom enchanting table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @since 0.11.0
+ */
+ public ContainerEnchantingTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+
+ super.checkReachable = false;
+
+ updateSlot(0, inputSlots);
+ updateSlot(1, inputSlots);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.inputSlots);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/GrindstoneInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..1fcb75659
--- /dev/null
+++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_17_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.GrindstoneMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal grindstone inventory for 1.17 R1
+ *
+ * @since 0.10.0
+ */
+public class GrindstoneInventoryImpl extends GrindstoneInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerGrindstoneImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryGrindstone(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.GRINDSTONE;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container grindstone
+ *
+ * @since 0.10.0
+ */
+ private static class ContainerGrindstoneImpl extends GrindstoneMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerGrindstoneImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryGrindstone inventory = new CraftInventoryGrindstone(this.itemsSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/MerchantInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/MerchantInventoryImpl.java
new file mode 100644
index 000000000..2cd7e9da2
--- /dev/null
+++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/MerchantInventoryImpl.java
@@ -0,0 +1,300 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_17_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil;
+import net.minecraft.network.chat.Component;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.entity.npc.ClientSideMerchant;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.MerchantContainer;
+import net.minecraft.world.inventory.MerchantMenu;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.trading.Merchant;
+import net.minecraft.world.item.trading.MerchantOffer;
+import net.minecraft.world.item.trading.MerchantOffers;
+import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MerchantRecipe;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal merchant inventory for 1.17.1
+ *
+ * @since 0.10.1
+ */
+public class MerchantInventoryImpl extends MerchantInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Merchant merchant = new ClientSideMerchant(null);
+
+ MerchantContainer container = new InventoryViewProvider(merchant) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull net.minecraft.world.entity.player.Player player
+ ) {
+ return new ContainerMerchantImpl(containerId, player, this, merchant);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryMerchant(merchant, container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.MERCHANT;
+ }
+
+ @Override
+ public MerchantContainer getInventory() {
+ return container;
+ }
+ };
+ }
+
+ @Override
+ public void sendMerchantOffers(@NotNull Player player,
+ @NotNull List extends Map.Entry extends MerchantRecipe, ? extends Integer>> trades,
+ int level, int experience) {
+ MerchantOffers offers = new MerchantOffers();
+
+ for (Map.Entry extends MerchantRecipe, ? extends Integer> entry : trades) {
+ MerchantRecipe recipe = entry.getKey();
+ List ingredients = recipe.getIngredients();
+
+ if (ingredients.size() < 1) {
+ throw new IllegalStateException("Merchant recipe has no ingredients");
+ }
+
+ ItemStack itemA = ingredients.get(0);
+ ItemStack itemB = null;
+
+ if (ingredients.size() >= 2) {
+ itemB = ingredients.get(1);
+ }
+
+ net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA);
+ net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY;
+ net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult());
+
+ if (itemB != null) {
+ nmsItemB = CraftItemStack.asNMSCopy(itemB);
+ }
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier
+ );
+ merchantOffer.setSpecialPriceDiff(entry.getValue());
+
+ offers.add(merchantOffer);
+ }
+
+ ServerPlayer serverPlayer = getServerPlayer(player);
+ int containerId = getContainerId(serverPlayer);
+
+ serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false);
+ }
+
+ /**
+ * Gets the server player associated to this player
+ *
+ * @param player the player to get the server player from
+ * @return the server player
+ * @since 0.10.1
+ */
+ @NotNull
+ @Contract(pure = true)
+ private ServerPlayer getServerPlayer(@NotNull Player player) {
+ return ((CraftPlayer) player).getHandle();
+ }
+
+ /**
+ * Gets the containerId id for the inventory view the player currently has open
+ *
+ * @param nmsPlayer the player to get the containerId id for
+ * @return the containerId id
+ * @since 0.10.1
+ */
+ @Contract(pure = true)
+ private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) {
+ return nmsPlayer.containerMenu.containerId;
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends MerchantContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public InventoryViewProvider(@NotNull Merchant merchant) {
+ super(merchant);
+ }
+ }
+
+ /**
+ * A custom container merchant
+ *
+ * @since 0.11.0
+ */
+ private static class ContainerMerchantImpl extends MerchantMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final MerchantContainer container;
+
+ /**
+ * The merchant.
+ */
+ @NotNull
+ private final Merchant merchant;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param container the items slots
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public ContainerMerchantImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull MerchantContainer container,
+ @NotNull Merchant merchant
+ ) {
+ super(containerId, player.getInventory(), merchant);
+
+ this.humanEntity = player.getBukkitEntity();
+ this.container = container;
+ this.merchant = merchant;
+
+ super.checkReachable = false;
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+
+ Slot slot = super.slots.get(2);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y) {
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPickup(@Nullable net.minecraft.world.entity.player.Player player) {
+ return false;
+ }
+
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPlace(@Nullable net.minecraft.world.item.ItemStack itemStack) {
+ return false;
+ }
+ };
+ newSlot.index = slot.index;
+
+ super.slots.set(2, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryMerchant inventory = new CraftInventoryMerchant(this.merchant, this.container);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable net.minecraft.world.entity.player.Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable net.minecraft.world.entity.player.Player player, @Nullable Container container) {}
+
+ @Override
+ public void setSelectionHint(int i) {}
+
+ @Override
+ public void tryMoveItems(int i) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/SmithingTableInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..786f3ed21
--- /dev/null
+++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/SmithingTableInventoryImpl.java
@@ -0,0 +1,216 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_17_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.ResultContainer;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.inventory.SmithingMenu;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal smithing table inventory for 1.17 R1
+ *
+ * @since 0.10.0
+ */
+public class SmithingTableInventoryImpl extends SmithingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ ResultContainer resultSlot = new ResultContainer();
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerSmithingTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventorySmithing(null, container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.SMITHING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container smithing table
+ *
+ * @since 0.10.0
+ */
+ private static class ContainerSmithingTableImpl extends SmithingMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final ResultContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom smithing table container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerSmithingTableImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull ResultContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventory inventory = new CraftInventorySmithing(
+ this.access.getLocation(),
+ this.itemsSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {}
+
+ @Override
+ protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) {
+ return true;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/StonecutterInventoryImpl.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..db6007ef1
--- /dev/null
+++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/StonecutterInventoryImpl.java
@@ -0,0 +1,200 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_17_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_17_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.*;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal stonecutter inventory for 1.17 R1
+ *
+ * @since 0.10.0
+ */
+public class StonecutterInventoryImpl extends StonecutterInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerStonecutterImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryStonecutter(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.STONECUTTER;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.0
+ */
+ private static class ContainerStonecutterImpl extends StonecutterMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom stonecutter container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerStonecutterImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer inputSlot,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlot = inputSlot;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlot, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryStonecutter inventory = new CraftInventoryStonecutter(this.inputSlot, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Contract(value = "_, _ -> false", pure = true)
+ @Override
+ public boolean clickMenuButton(@Nullable net.minecraft.world.entity.player.Player player, int index) {
+ return false;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/CustomInventoryUtil.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..4959cb8b6
--- /dev/null
+++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_17_1.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A utility class for custom inventories
+ *
+ * @since 0.10.0
+ */
+public final class CustomInventoryUtil {
+
+ /**
+ * A private constructor to prevent construction.
+ */
+ private CustomInventoryUtil() {}
+
+ /**
+ * Converts an array of Bukkit items into a non null list of NMS items. The returned list is modifiable. If no items
+ * were specified, this returns an empty list.
+ *
+ * @param items the items to convert
+ * @return a list of converted items
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) {
+ NonNullList nmsItems = NonNullList.create();
+
+ for (org.bukkit.inventory.ItemStack item : items) {
+ nmsItems.add(CraftItemStack.asNMSCopy(item));
+ }
+
+ return nmsItems;
+ }
+}
diff --git a/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/TextHolderUtil.java b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/TextHolderUtil.java
new file mode 100644
index 000000000..9615b0b52
--- /dev/null
+++ b/nms/1_17_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_17_1/util/TextHolderUtil.java
@@ -0,0 +1,66 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_17_1.util;
+
+import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.TextComponent;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * A utility class for adding {@link TextHolder} support.
+ *
+ * @since 0.10.0
+ */
+public final class TextHolderUtil {
+
+ private TextHolderUtil() {
+ //private constructor to prevent construction
+ }
+
+ /**
+ * Converts the specified value to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static Component toComponent(@NotNull TextHolder holder) {
+ if (holder instanceof StringHolder) {
+ return toComponent((StringHolder) holder);
+ } else {
+ return toComponent((ComponentHolder) holder);
+ }
+ }
+
+ /**
+ * Converts the specified legacy string holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull StringHolder holder) {
+ return new TextComponent(holder.asLegacyString());
+ }
+
+ /**
+ * Converts the specified Adventure component holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull ComponentHolder holder) {
+ return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson()));
+ }
+}
diff --git a/nms/1_18_2/pom.xml b/nms/1_18_2/pom.xml
new file mode 100644
index 000000000..98c42ef08
--- /dev/null
+++ b/nms/1_18_2/pom.xml
@@ -0,0 +1,59 @@
+
+
+
+ IF-parent
+ com.github.stefvanschie.inventoryframework
+ 0.12.0
+ ../../pom.xml
+
+ 4.0.0
+
+ 1_18_2
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ ca.bkaw
+ paper-nms
+ 1.18.2-SNAPSHOT
+ provided
+
+
+
+
+
+
+ ca.bkaw
+ paper-nms-maven-plugin
+ 1.4.10
+
+
+ process-classes
+
+ remap
+
+
+
+
+
+
+
+
+
+ bytecode.space
+ https://repo.bytecode.space/repository/maven-public/
+
+
+
+
\ No newline at end of file
diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/AnvilInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/AnvilInventoryImpl.java
new file mode 100644
index 000000000..d4dde5e37
--- /dev/null
+++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/AnvilInventoryImpl.java
@@ -0,0 +1,228 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_18_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.AnvilMenu;
+import net.minecraft.world.inventory.ClickType;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal anvil inventory for 1.18.2
+ *
+ * @since 0.10.5
+ */
+public class AnvilInventoryImpl extends AnvilInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer inputSlots = new SimpleContainer(2);
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ return new CraftInventoryAnvil(null, inputSlots, resultSlot, null) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ANVIL;
+ }
+
+ @Override
+ public Container getInventory() {
+ return new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerAnvilImpl(containerId, player, inputSlots, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {}
+
+ /**
+ * A custom container anvil for responding to item renaming
+ *
+ * @since 0.10.5
+ */
+ private class ContainerAnvilImpl extends AnvilMenu {
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom anvil container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerAnvilImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.getLevel(), BlockPos.ZERO));
+
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ this.checkReachable = false;
+ this.cost.set(AnvilInventoryImpl.super.cost);
+
+ CompoundContainer compoundContainer = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, compoundContainer);
+ updateSlot(1, compoundContainer);
+ updateSlot(2, compoundContainer);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(
+ this.access.getLocation(),
+ this.inputSlots,
+ this.resultSlot,
+ this
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void broadcastChanges() {
+ if (super.cost.checkAndClearUpdateFlag()) {
+ broadcastFullState();
+ } else {
+ for (int index = 0; index < super.slots.size(); index++) {
+ if (!ItemStack.matches(super.remoteSlots.get(index), super.slots.get(index).getItem())) {
+ broadcastFullState();
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setItemName(@Nullable String name) {
+ name = name == null ? "" : name;
+
+ /* Only update if the name is actually different. This may be called even if the name is not different,
+ particularly when putting an item in the first slot. */
+ if (!name.equals(AnvilInventoryImpl.super.observableText.get())) {
+ AnvilInventoryImpl.super.observableText.set(name);
+ }
+
+ //the client predicts the output result, so we broadcast the state again to override it
+ broadcastFullState();
+ }
+
+ @Override
+ public void slotsChanged(@NotNull Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, @NotNull ClickType clickType, @NotNull Player player) {
+ super.clicked(index, dragData, clickType, player);
+
+ //client predicts first slot, so send data to override
+ broadcastFullState();
+ }
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ public void removed(@NotNull Player nmsPlayer) {}
+
+ @Override
+ protected void clearContainer(@NotNull Player player, @NotNull Container inventory) {}
+
+ @Override
+ protected void onTake(@NotNull Player player, @NotNull ItemStack stack) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/BeaconInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/BeaconInventoryImpl.java
new file mode 100644
index 000000000..083a8d68a
--- /dev/null
+++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/BeaconInventoryImpl.java
@@ -0,0 +1,162 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_18_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.TextComponent;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.BeaconMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.SimpleContainerData;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal beacon inventory for 1.18.2
+ *
+ * @since 0.10.5
+ */
+public class BeaconInventoryImpl extends BeaconInventory {
+
+ @NotNull
+ @Override
+ public Inventory createInventory() {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerBeaconImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return new TextComponent("Beacon");
+ }
+ };
+
+ container.setMaxStackSize(1); //client limitation
+
+ return new CraftInventoryBeacon(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.BEACON;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with one slot.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container beacon
+ *
+ * @since 0.10.5
+ */
+ private static class ContainerBeaconImpl extends BeaconMenu {
+
+ /**
+ * The player viewing this menu.
+ */
+ @NotNull
+ private final Player player;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom beacon container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @since 0.11.0
+ */
+ public ContainerBeaconImpl(int containerId, @NotNull Player player, @NotNull SimpleContainer inputSlot) {
+ super(containerId, player.getInventory(), new SimpleContainerData(3),
+ ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.player = player;
+ this.inputSlot = inputSlot;
+
+ super.checkReachable = false;
+
+ Slot slot = super.slots.get(0);
+
+ Slot newSlot = new Slot(inputSlot, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(0, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryBeacon inventory = new CraftInventoryBeacon(this.inputSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ }
+}
diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/CartographyTableInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..43e04c2cf
--- /dev/null
+++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/CartographyTableInventoryImpl.java
@@ -0,0 +1,196 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_18_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.CartographyTableMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal cartography table inventory for 1.18.2
+ *
+ * @since 0.10.5
+ */
+public class CartographyTableInventoryImpl extends CartographyTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerCartographyTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryCartography(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.CARTOGRAPHY;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container cartography table
+ *
+ * @since 0.10.5
+ */
+ private static class ContainerCartographyTableImpl extends CartographyTableMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom cartography table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerCartographyTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryCartography inventory = new CraftInventoryCartography(this.inputSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+
+ }
+}
diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/EnchantingTableInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..afb6c25d5
--- /dev/null
+++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/EnchantingTableInventoryImpl.java
@@ -0,0 +1,180 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_18_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.EnchantmentMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal enchanting table inventory for 1.18.2
+ *
+ * @since 0.10.5
+ */
+public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerEnchantingTableImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryEnchanting(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ENCHANTING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.5
+ */
+ private static class ContainerEnchantingTableImpl extends EnchantmentMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom enchanting table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @since 0.11.0
+ */
+ public ContainerEnchantingTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+
+ super.checkReachable = false;
+
+ updateSlot(0, inputSlots);
+ updateSlot(1, inputSlots);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.inputSlots);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/GrindstoneInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..0a708b49e
--- /dev/null
+++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_18_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.GrindstoneMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal grindstone inventory for 1.18.2
+ *
+ * @since 0.10.5
+ */
+public class GrindstoneInventoryImpl extends GrindstoneInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerGrindstoneImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryGrindstone(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.GRINDSTONE;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container grindstone
+ *
+ * @since 0.10.5
+ */
+ private static class ContainerGrindstoneImpl extends GrindstoneMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerGrindstoneImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryGrindstone inventory = new CraftInventoryGrindstone(this.itemsSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/MerchantInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/MerchantInventoryImpl.java
new file mode 100644
index 000000000..a7a729401
--- /dev/null
+++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/MerchantInventoryImpl.java
@@ -0,0 +1,300 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_18_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil;
+import net.minecraft.network.chat.Component;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.entity.npc.ClientSideMerchant;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.MerchantContainer;
+import net.minecraft.world.inventory.MerchantMenu;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.trading.Merchant;
+import net.minecraft.world.item.trading.MerchantOffer;
+import net.minecraft.world.item.trading.MerchantOffers;
+import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MerchantRecipe;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal merchant inventory for 1.18.2
+ *
+ * @since 0.10.5
+ */
+public class MerchantInventoryImpl extends MerchantInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Merchant merchant = new ClientSideMerchant(null);
+
+ MerchantContainer container = new InventoryViewProvider(merchant) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull net.minecraft.world.entity.player.Player player
+ ) {
+ return new ContainerMerchantImpl(containerId, player, this, merchant);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryMerchant(merchant, container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.MERCHANT;
+ }
+
+ @Override
+ public MerchantContainer getInventory() {
+ return container;
+ }
+ };
+ }
+
+ @Override
+ public void sendMerchantOffers(@NotNull Player player,
+ @NotNull List extends Map.Entry extends MerchantRecipe, ? extends Integer>> trades,
+ int level, int experience) {
+ MerchantOffers offers = new MerchantOffers();
+
+ for (Map.Entry extends MerchantRecipe, ? extends Integer> entry : trades) {
+ MerchantRecipe recipe = entry.getKey();
+ List ingredients = recipe.getIngredients();
+
+ if (ingredients.size() < 1) {
+ throw new IllegalStateException("Merchant recipe has no ingredients");
+ }
+
+ ItemStack itemA = ingredients.get(0);
+ ItemStack itemB = null;
+
+ if (ingredients.size() >= 2) {
+ itemB = ingredients.get(1);
+ }
+
+ net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA);
+ net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY;
+ net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult());
+
+ if (itemB != null) {
+ nmsItemB = CraftItemStack.asNMSCopy(itemB);
+ }
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier
+ );
+ merchantOffer.setSpecialPriceDiff(entry.getValue());
+
+ offers.add(merchantOffer);
+ }
+
+ ServerPlayer serverPlayer = getServerPlayer(player);
+ int containerId = getContainerId(serverPlayer);
+
+ serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false);
+ }
+
+ /**
+ * Gets the server player associated to this player
+ *
+ * @param player the player to get the server player from
+ * @return the server player
+ * @since 0.10.5
+ */
+ @NotNull
+ @Contract(pure = true)
+ private ServerPlayer getServerPlayer(@NotNull Player player) {
+ return ((CraftPlayer) player).getHandle();
+ }
+
+ /**
+ * Gets the containerId id for the inventory view the player currently has open
+ *
+ * @param nmsPlayer the player to get the containerId id for
+ * @return the containerId id
+ * @since 0.10.5
+ */
+ @Contract(pure = true)
+ private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) {
+ return nmsPlayer.containerMenu.containerId;
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends MerchantContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public InventoryViewProvider(@NotNull Merchant merchant) {
+ super(merchant);
+ }
+ }
+
+ /**
+ * A custom container merchant
+ *
+ * @since 0.11.0
+ */
+ private static class ContainerMerchantImpl extends MerchantMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final MerchantContainer container;
+
+ /**
+ * The merchant.
+ */
+ @NotNull
+ private final Merchant merchant;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param container the items slots
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public ContainerMerchantImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull MerchantContainer container,
+ @NotNull Merchant merchant
+ ) {
+ super(containerId, player.getInventory(), merchant);
+
+ this.humanEntity = player.getBukkitEntity();
+ this.container = container;
+ this.merchant = merchant;
+
+ super.checkReachable = false;
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+
+ Slot slot = super.slots.get(2);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y) {
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPickup(@Nullable net.minecraft.world.entity.player.Player player) {
+ return false;
+ }
+
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPlace(@Nullable net.minecraft.world.item.ItemStack itemStack) {
+ return false;
+ }
+ };
+ newSlot.index = slot.index;
+
+ super.slots.set(2, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryMerchant inventory = new CraftInventoryMerchant(this.merchant, this.container);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable net.minecraft.world.entity.player.Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable net.minecraft.world.entity.player.Player player, @Nullable Container container) {}
+
+ @Override
+ public void setSelectionHint(int i) {}
+
+ @Override
+ public void tryMoveItems(int i) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/SmithingTableInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..2d5c1cbaa
--- /dev/null
+++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/SmithingTableInventoryImpl.java
@@ -0,0 +1,216 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_18_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.ResultContainer;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.inventory.SmithingMenu;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal smithing table inventory for 1.18.2
+ *
+ * @since 0.10.5
+ */
+public class SmithingTableInventoryImpl extends SmithingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ ResultContainer resultSlot = new ResultContainer();
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerSmithingTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventorySmithing(null, container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.SMITHING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container smithing table
+ *
+ * @since 0.10.5
+ */
+ private static class ContainerSmithingTableImpl extends SmithingMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final ResultContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom smithing table container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerSmithingTableImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull ResultContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventory inventory = new CraftInventorySmithing(
+ this.access.getLocation(),
+ this.itemsSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {}
+
+ @Override
+ protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) {
+ return true;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/StonecutterInventoryImpl.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..a574ad1c5
--- /dev/null
+++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/StonecutterInventoryImpl.java
@@ -0,0 +1,200 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_18_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_18_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.*;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal stonecutter inventory for 1.18.2
+ *
+ * @since 0.10.5
+ */
+public class StonecutterInventoryImpl extends StonecutterInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerStonecutterImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryStonecutter(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.STONECUTTER;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.5
+ */
+ private static class ContainerStonecutterImpl extends StonecutterMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom stonecutter container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerStonecutterImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer inputSlot,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlot = inputSlot;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlot, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryStonecutter inventory = new CraftInventoryStonecutter(this.inputSlot, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Contract(value = "_, _ -> false", pure = true)
+ @Override
+ public boolean clickMenuButton(@Nullable net.minecraft.world.entity.player.Player player, int index) {
+ return false;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/CustomInventoryUtil.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..1f20cc3ca
--- /dev/null
+++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_18_2.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A utility class for custom inventories
+ *
+ * @since 0.10.5
+ */
+public final class CustomInventoryUtil {
+
+ /**
+ * A private constructor to prevent construction.
+ */
+ private CustomInventoryUtil() {}
+
+ /**
+ * Converts an array of Bukkit items into a non null list of NMS items. The returned list is modifiable. If no items
+ * were specified, this returns an empty list.
+ *
+ * @param items the items to convert
+ * @return a list of converted items
+ * @since 0.10.5
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) {
+ NonNullList nmsItems = NonNullList.create();
+
+ for (org.bukkit.inventory.ItemStack item : items) {
+ nmsItems.add(CraftItemStack.asNMSCopy(item));
+ }
+
+ return nmsItems;
+ }
+}
diff --git a/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/TextHolderUtil.java b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/TextHolderUtil.java
new file mode 100644
index 000000000..2e2fe9542
--- /dev/null
+++ b/nms/1_18_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_18_2/util/TextHolderUtil.java
@@ -0,0 +1,66 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_18_2.util;
+
+import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.TextComponent;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * A utility class for adding {@link TextHolder} support.
+ *
+ * @since 0.10.5
+ */
+public final class TextHolderUtil {
+
+ private TextHolderUtil() {
+ //private constructor to prevent construction
+ }
+
+ /**
+ * Converts the specified value to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.5
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static Component toComponent(@NotNull TextHolder holder) {
+ if (holder instanceof StringHolder) {
+ return toComponent((StringHolder) holder);
+ } else {
+ return toComponent((ComponentHolder) holder);
+ }
+ }
+
+ /**
+ * Converts the specified legacy string holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.5
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull StringHolder holder) {
+ return new TextComponent(holder.asLegacyString());
+ }
+
+ /**
+ * Converts the specified Adventure component holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.5
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull ComponentHolder holder) {
+ return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson()));
+ }
+}
diff --git a/nms/1_19_4/pom.xml b/nms/1_19_4/pom.xml
new file mode 100644
index 000000000..250060a52
--- /dev/null
+++ b/nms/1_19_4/pom.xml
@@ -0,0 +1,58 @@
+
+
+
+ IF-parent
+ com.github.stefvanschie.inventoryframework
+ 0.12.0
+ ../../pom.xml
+
+ 4.0.0
+
+ 1_19_4
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ ca.bkaw
+ paper-nms
+ 1.19.4-SNAPSHOT
+ provided
+
+
+
+
+
+
+ ca.bkaw
+ paper-nms-maven-plugin
+ 1.4.10
+
+
+ process-classes
+
+ remap
+
+
+
+
+
+
+
+
+
+ bytecode.space
+ https://repo.bytecode.space/repository/maven-public/
+
+
+
\ No newline at end of file
diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/AnvilInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/AnvilInventoryImpl.java
new file mode 100644
index 000000000..f73f294bf
--- /dev/null
+++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/AnvilInventoryImpl.java
@@ -0,0 +1,228 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_19_4;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.AnvilMenu;
+import net.minecraft.world.inventory.ClickType;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal anvil inventory for 1.19.4
+ *
+ * @since 0.10.9
+ */
+public class AnvilInventoryImpl extends AnvilInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer inputSlots = new SimpleContainer(2);
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ return new CraftInventoryAnvil(null, inputSlots, resultSlot, null) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ANVIL;
+ }
+
+ @Override
+ public Container getInventory() {
+ return new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerAnvilImpl(containerId, player, inputSlots, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {}
+
+ /**
+ * A custom container anvil for responding to item renaming
+ *
+ * @since 0.10.9
+ */
+ private class ContainerAnvilImpl extends AnvilMenu {
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom anvil container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerAnvilImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.getLevel(), BlockPos.ZERO));
+
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ this.checkReachable = false;
+ this.cost.set(AnvilInventoryImpl.super.cost);
+
+ CompoundContainer compoundContainer = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, compoundContainer);
+ updateSlot(1, compoundContainer);
+ updateSlot(2, compoundContainer);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(
+ this.access.getLocation(),
+ this.inputSlots,
+ this.resultSlot,
+ this
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void broadcastChanges() {
+ if (super.cost.checkAndClearUpdateFlag()) {
+ broadcastFullState();
+ } else {
+ for (int index = 0; index < super.slots.size(); index++) {
+ if (!ItemStack.matches(super.remoteSlots.get(index), super.slots.get(index).getItem())) {
+ broadcastFullState();
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setItemName(@Nullable String name) {
+ name = name == null ? "" : name;
+
+ /* Only update if the name is actually different. This may be called even if the name is not different,
+ particularly when putting an item in the first slot. */
+ if (!name.equals(AnvilInventoryImpl.super.observableText.get())) {
+ AnvilInventoryImpl.super.observableText.set(name);
+ }
+
+ //the client predicts the output result, so we broadcast the state again to override it
+ broadcastFullState();
+ }
+
+ @Override
+ public void slotsChanged(@NotNull Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, @NotNull ClickType clickType, @NotNull Player player) {
+ super.clicked(index, dragData, clickType, player);
+
+ //client predicts first slot, so send data to override
+ broadcastFullState();
+ }
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ public void removed(@NotNull Player nmsPlayer) {}
+
+ @Override
+ protected void clearContainer(@NotNull Player player, @NotNull Container inventory) {}
+
+ @Override
+ protected void onTake(@NotNull Player player, @NotNull ItemStack stack) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/BeaconInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/BeaconInventoryImpl.java
new file mode 100644
index 000000000..1a4357b4f
--- /dev/null
+++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/BeaconInventoryImpl.java
@@ -0,0 +1,161 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_19_4;
+
+import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.BeaconMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.SimpleContainerData;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal beacon inventory for 1.19.4
+ *
+ * @since 0.10.9
+ */
+public class BeaconInventoryImpl extends BeaconInventory {
+
+ @NotNull
+ @Override
+ public Inventory createInventory() {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerBeaconImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return Component.literal("Beacon");
+ }
+ };
+
+ container.setMaxStackSize(1); //client limitation
+
+ return new CraftInventoryBeacon(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.BEACON;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with one slot.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container beacon
+ *
+ * @since 0.10.9
+ */
+ private static class ContainerBeaconImpl extends BeaconMenu {
+
+ /**
+ * The player viewing this menu.
+ */
+ @NotNull
+ private final Player player;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom beacon container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @since 0.11.0
+ */
+ public ContainerBeaconImpl(int containerId, @NotNull Player player, @NotNull SimpleContainer inputSlot) {
+ super(containerId, player.getInventory(), new SimpleContainerData(3),
+ ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.player = player;
+ this.inputSlot = inputSlot;
+
+ super.checkReachable = false;
+
+ Slot slot = super.slots.get(0);
+
+ Slot newSlot = new Slot(inputSlot, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(0, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryBeacon inventory = new CraftInventoryBeacon(this.inputSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ }
+}
diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/CartographyTableInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..ff23cf36b
--- /dev/null
+++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/CartographyTableInventoryImpl.java
@@ -0,0 +1,196 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_19_4;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.CartographyTableMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal cartography table inventory for 1.19.4
+ *
+ * @since 0.10.9
+ */
+public class CartographyTableInventoryImpl extends CartographyTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerCartographyTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryCartography(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.CARTOGRAPHY;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container cartography table
+ *
+ * @since 0.10.9
+ */
+ private static class ContainerCartographyTableImpl extends CartographyTableMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom cartography table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerCartographyTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryCartography inventory = new CraftInventoryCartography(this.inputSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+
+ }
+}
diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/EnchantingTableInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..373598e97
--- /dev/null
+++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/EnchantingTableInventoryImpl.java
@@ -0,0 +1,180 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_19_4;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.EnchantmentMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal enchanting table inventory for 1.19.4
+ *
+ * @since 0.10.9
+ */
+public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerEnchantingTableImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryEnchanting(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ENCHANTING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.9
+ */
+ private static class ContainerEnchantingTableImpl extends EnchantmentMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom enchanting table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @since 0.11.0
+ */
+ public ContainerEnchantingTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+
+ super.checkReachable = false;
+
+ updateSlot(0, inputSlots);
+ updateSlot(1, inputSlots);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.inputSlots);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/GrindstoneInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..665123b2b
--- /dev/null
+++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_19_4;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.GrindstoneMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal grindstone inventory for 1.19.4
+ *
+ * @since 0.10.9
+ */
+public class GrindstoneInventoryImpl extends GrindstoneInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerGrindstoneImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryGrindstone(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.GRINDSTONE;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container grindstone
+ *
+ * @since 0.10.9
+ */
+ private static class ContainerGrindstoneImpl extends GrindstoneMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerGrindstoneImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryGrindstone inventory = new CraftInventoryGrindstone(this.itemsSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/LegacySmithingTableInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/LegacySmithingTableInventoryImpl.java
new file mode 100644
index 000000000..d7779127c
--- /dev/null
+++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/LegacySmithingTableInventoryImpl.java
@@ -0,0 +1,218 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_19_4;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.LegacySmithingMenu;
+import net.minecraft.world.inventory.ResultContainer;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal smithing table inventory for 1.19.4. This smithing table is for prior to Minecraft 1.20.
+ *
+ * @since 0.10.9
+ * @deprecated this type of smithing table will be removed in Minecraft 1.20
+ */
+@Deprecated
+public class LegacySmithingTableInventoryImpl extends SmithingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ ResultContainer resultSlot = new ResultContainer();
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerSmithingTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventorySmithing(null, container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.SMITHING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container smithing table
+ *
+ * @since 0.10.9
+ */
+ private static class ContainerSmithingTableImpl extends LegacySmithingMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final ResultContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom smithing table container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerSmithingTableImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull ResultContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventory inventory = new CraftInventorySmithing(
+ this.access.getLocation(),
+ this.itemsSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {}
+
+ @Override
+ protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) {
+ return true;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/MerchantInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/MerchantInventoryImpl.java
new file mode 100644
index 000000000..b366305b7
--- /dev/null
+++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/MerchantInventoryImpl.java
@@ -0,0 +1,300 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_19_4;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil;
+import net.minecraft.network.chat.Component;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.entity.npc.ClientSideMerchant;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.MerchantContainer;
+import net.minecraft.world.inventory.MerchantMenu;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.trading.Merchant;
+import net.minecraft.world.item.trading.MerchantOffer;
+import net.minecraft.world.item.trading.MerchantOffers;
+import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MerchantRecipe;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal merchant inventory for 1.19.4
+ *
+ * @since 0.10.9
+ */
+public class MerchantInventoryImpl extends MerchantInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Merchant merchant = new ClientSideMerchant(null);
+
+ MerchantContainer container = new InventoryViewProvider(merchant) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull net.minecraft.world.entity.player.Player player
+ ) {
+ return new ContainerMerchantImpl(containerId, player, this, merchant);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryMerchant(merchant, container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.MERCHANT;
+ }
+
+ @Override
+ public MerchantContainer getInventory() {
+ return container;
+ }
+ };
+ }
+
+ @Override
+ public void sendMerchantOffers(@NotNull Player player,
+ @NotNull List extends Map.Entry extends MerchantRecipe, ? extends Integer>> trades,
+ int level, int experience) {
+ MerchantOffers offers = new MerchantOffers();
+
+ for (Map.Entry extends MerchantRecipe, ? extends Integer> entry : trades) {
+ MerchantRecipe recipe = entry.getKey();
+ List ingredients = recipe.getIngredients();
+
+ if (ingredients.size() < 1) {
+ throw new IllegalStateException("Merchant recipe has no ingredients");
+ }
+
+ ItemStack itemA = ingredients.get(0);
+ ItemStack itemB = null;
+
+ if (ingredients.size() >= 2) {
+ itemB = ingredients.get(1);
+ }
+
+ net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA);
+ net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY;
+ net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult());
+
+ if (itemB != null) {
+ nmsItemB = CraftItemStack.asNMSCopy(itemB);
+ }
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier
+ );
+ merchantOffer.setSpecialPriceDiff(entry.getValue());
+
+ offers.add(merchantOffer);
+ }
+
+ ServerPlayer serverPlayer = getServerPlayer(player);
+ int containerId = getContainerId(serverPlayer);
+
+ serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false);
+ }
+
+ /**
+ * Gets the server player associated to this player
+ *
+ * @param player the player to get the server player from
+ * @return the server player
+ * @since 0.10.9
+ */
+ @NotNull
+ @Contract(pure = true)
+ private ServerPlayer getServerPlayer(@NotNull Player player) {
+ return ((CraftPlayer) player).getHandle();
+ }
+
+ /**
+ * Gets the containerId id for the inventory view the player currently has open
+ *
+ * @param nmsPlayer the player to get the containerId id for
+ * @return the containerId id
+ * @since 0.10.9
+ */
+ @Contract(pure = true)
+ private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) {
+ return nmsPlayer.containerMenu.containerId;
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends MerchantContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public InventoryViewProvider(@NotNull Merchant merchant) {
+ super(merchant);
+ }
+ }
+
+ /**
+ * A custom container merchant
+ *
+ * @since 0.11.0
+ */
+ private static class ContainerMerchantImpl extends MerchantMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final MerchantContainer container;
+
+ /**
+ * The merchant.
+ */
+ @NotNull
+ private final Merchant merchant;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param container the items slots
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public ContainerMerchantImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull MerchantContainer container,
+ @NotNull Merchant merchant
+ ) {
+ super(containerId, player.getInventory(), merchant);
+
+ this.humanEntity = player.getBukkitEntity();
+ this.container = container;
+ this.merchant = merchant;
+
+ super.checkReachable = false;
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+
+ Slot slot = super.slots.get(2);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y) {
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPickup(@Nullable net.minecraft.world.entity.player.Player player) {
+ return false;
+ }
+
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPlace(@Nullable net.minecraft.world.item.ItemStack itemStack) {
+ return false;
+ }
+ };
+ newSlot.index = slot.index;
+
+ super.slots.set(2, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryMerchant inventory = new CraftInventoryMerchant(this.merchant, this.container);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable net.minecraft.world.entity.player.Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable net.minecraft.world.entity.player.Player player, @Nullable Container container) {}
+
+ @Override
+ public void setSelectionHint(int i) {}
+
+ @Override
+ public void tryMoveItems(int i) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/SmithingTableInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..6684733eb
--- /dev/null
+++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/SmithingTableInventoryImpl.java
@@ -0,0 +1,218 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_19_4;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.ResultContainer;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.inventory.SmithingMenu;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventorySmithingNew;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal smithing table inventory for 1.19.4. This smithing table is for Minecraft 1.19.4 or higher.
+ *
+ * @since 0.10.9
+ */
+public class SmithingTableInventoryImpl extends SmithingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ ResultContainer resultSlot = new ResultContainer();
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerSmithingTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventorySmithing(null, container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.SMITHING_NEW;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(3);
+ }
+ }
+
+ /**
+ * A custom container smithing table
+ *
+ * @since 0.10.9
+ */
+ private static class ContainerSmithingTableImpl extends SmithingMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final ResultContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom smithing table container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerSmithingTableImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull ResultContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ updateSlot(3, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventory inventory = new CraftInventorySmithingNew(
+ this.access.getLocation(),
+ this.itemsSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {}
+
+ @Override
+ protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) {
+ return true;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/StonecutterInventoryImpl.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..bf902671a
--- /dev/null
+++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/StonecutterInventoryImpl.java
@@ -0,0 +1,200 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_19_4;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_19_4.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.*;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal stonecutter inventory for 1.19.4
+ *
+ * @since 0.10.9
+ */
+public class StonecutterInventoryImpl extends StonecutterInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerStonecutterImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryStonecutter(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.STONECUTTER;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.9
+ */
+ private static class ContainerStonecutterImpl extends StonecutterMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom stonecutter container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerStonecutterImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer inputSlot,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level, BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlot = inputSlot;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlot, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryStonecutter inventory = new CraftInventoryStonecutter(this.inputSlot, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Contract(value = "_, _ -> false", pure = true)
+ @Override
+ public boolean clickMenuButton(@Nullable net.minecraft.world.entity.player.Player player, int index) {
+ return false;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/CustomInventoryUtil.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..317736273
--- /dev/null
+++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_19_4.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A utility class for custom inventories
+ *
+ * @since 0.10.
+ */
+public final class CustomInventoryUtil {
+
+ /**
+ * A private constructor to prevent construction.
+ */
+ private CustomInventoryUtil() {}
+
+ /**
+ * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items
+ * were specified, this returns an empty list.
+ *
+ * @param items the items to convert
+ * @return a list of converted items
+ * @since 0.10.9
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) {
+ NonNullList nmsItems = NonNullList.create();
+
+ for (org.bukkit.inventory.ItemStack item : items) {
+ nmsItems.add(CraftItemStack.asNMSCopy(item));
+ }
+
+ return nmsItems;
+ }
+}
diff --git a/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/TextHolderUtil.java b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/TextHolderUtil.java
new file mode 100644
index 000000000..bf999b34d
--- /dev/null
+++ b/nms/1_19_4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_19_4/util/TextHolderUtil.java
@@ -0,0 +1,65 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_19_4.util;
+
+import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * A utility class for adding {@link TextHolder} support.
+ *
+ * @since 0.10.9
+ */
+public final class TextHolderUtil {
+
+ private TextHolderUtil() {
+ //private constructor to prevent construction
+ }
+
+ /**
+ * Converts the specified value to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.9
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static Component toComponent(@NotNull TextHolder holder) {
+ if (holder instanceof StringHolder) {
+ return toComponent((StringHolder) holder);
+ } else {
+ return toComponent((ComponentHolder) holder);
+ }
+ }
+
+ /**
+ * Converts the specified legacy string holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.9
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull StringHolder holder) {
+ return Component.literal(holder.asLegacyString());
+ }
+
+ /**
+ * Converts the specified Adventure component holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.9
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull ComponentHolder holder) {
+ return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson()));
+ }
+}
diff --git a/nms/1_20_0/pom.xml b/nms/1_20_0/pom.xml
new file mode 100644
index 000000000..5401e058c
--- /dev/null
+++ b/nms/1_20_0/pom.xml
@@ -0,0 +1,58 @@
+
+
+ 4.0.0
+
+ com.github.stefvanschie.inventoryframework
+ IF-parent
+ 0.12.0
+ ../../pom.xml
+
+
+ 1_20_0
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ ca.bkaw
+ paper-nms
+ 1.20-SNAPSHOT
+ provided
+
+
+
+
+
+
+ ca.bkaw
+ paper-nms-maven-plugin
+ 1.4.10
+
+
+ process-classes
+
+ remap
+
+
+
+
+
+
+
+
+
+ bytecode.space
+ https://repo.bytecode.space/repository/maven-public/
+
+
+
\ No newline at end of file
diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/AnvilInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/AnvilInventoryImpl.java
new file mode 100644
index 000000000..b6e06ce66
--- /dev/null
+++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/AnvilInventoryImpl.java
@@ -0,0 +1,229 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.AnvilMenu;
+import net.minecraft.world.inventory.ClickType;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal anvil inventory for 1.20.0
+ *
+ * @since 0.10.14
+ */
+public class AnvilInventoryImpl extends AnvilInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer inputSlots = new SimpleContainer(2);
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ return new CraftInventoryAnvil(null, inputSlots, resultSlot, null) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ANVIL;
+ }
+
+ @Override
+ public Container getInventory() {
+ return new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerAnvilImpl(containerId, player, inputSlots, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {}
+
+ /**
+ * A custom container anvil for responding to item renaming
+ *
+ * @since 0.10.14
+ */
+ private class ContainerAnvilImpl extends AnvilMenu {
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom anvil container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerAnvilImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ this.checkReachable = false;
+ this.cost.set(AnvilInventoryImpl.super.cost);
+
+ CompoundContainer compoundContainer = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, compoundContainer);
+ updateSlot(1, compoundContainer);
+ updateSlot(2, compoundContainer);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(
+ this.access.getLocation(),
+ this.inputSlots,
+ this.resultSlot,
+ this
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void broadcastChanges() {
+ if (super.cost.checkAndClearUpdateFlag()) {
+ broadcastFullState();
+ } else {
+ for (int index = 0; index < super.slots.size(); index++) {
+ if (!ItemStack.matches(super.remoteSlots.get(index), super.slots.get(index).getItem())) {
+ broadcastFullState();
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean setItemName(@Nullable String name) {
+ name = name == null ? "" : name;
+
+ /* Only update if the name is actually different. This may be called even if the name is not different,
+ particularly when putting an item in the first slot. */
+ if (!name.equals(AnvilInventoryImpl.super.observableText.get())) {
+ AnvilInventoryImpl.super.observableText.set(name);
+ }
+
+ //the client predicts the output result, so we broadcast the state again to override it
+ broadcastFullState();
+ return true; //no idea what this is for
+ }
+
+ @Override
+ public void slotsChanged(@NotNull Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, @NotNull ClickType clickType, @NotNull Player player) {
+ super.clicked(index, dragData, clickType, player);
+
+ //client predicts first slot, so send data to override
+ broadcastFullState();
+ }
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ public void removed(@NotNull Player nmsPlayer) {}
+
+ @Override
+ protected void clearContainer(@NotNull Player player, @NotNull Container inventory) {}
+
+ @Override
+ protected void onTake(@NotNull Player player, @NotNull ItemStack stack) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/BeaconInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/BeaconInventoryImpl.java
new file mode 100644
index 000000000..771895278
--- /dev/null
+++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/BeaconInventoryImpl.java
@@ -0,0 +1,161 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.BeaconMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.SimpleContainerData;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal beacon inventory for 1.20.0
+ *
+ * @since 0.10.14
+ */
+public class BeaconInventoryImpl extends BeaconInventory {
+
+ @NotNull
+ @Override
+ public Inventory createInventory() {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerBeaconImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return Component.literal("Beacon");
+ }
+ };
+
+ container.setMaxStackSize(1); //client limitation
+
+ return new CraftInventoryBeacon(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.BEACON;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with one slot.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container beacon
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerBeaconImpl extends BeaconMenu {
+
+ /**
+ * The player viewing this menu.
+ */
+ @NotNull
+ private final Player player;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom beacon container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @since 0.11.0
+ */
+ public ContainerBeaconImpl(int containerId, @NotNull Player player, @NotNull SimpleContainer inputSlot) {
+ super(containerId, player.getInventory(), new SimpleContainerData(3),
+ ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.player = player;
+ this.inputSlot = inputSlot;
+
+ super.checkReachable = false;
+
+ Slot slot = super.slots.get(0);
+
+ Slot newSlot = new Slot(inputSlot, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(0, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryBeacon inventory = new CraftInventoryBeacon(this.inputSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ }
+}
diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/CartographyTableInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..96434ae6c
--- /dev/null
+++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/CartographyTableInventoryImpl.java
@@ -0,0 +1,196 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.CartographyTableMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal cartography table inventory for 1.20.0
+ *
+ * @since 0.10.14
+ */
+public class CartographyTableInventoryImpl extends CartographyTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerCartographyTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryCartography(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.CARTOGRAPHY;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container cartography table
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerCartographyTableImpl extends CartographyTableMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom cartography table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerCartographyTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryCartography inventory = new CraftInventoryCartography(this.inputSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+
+ }
+}
diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/EnchantingTableInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..a724028ac
--- /dev/null
+++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/EnchantingTableInventoryImpl.java
@@ -0,0 +1,180 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.EnchantmentMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal enchanting table inventory for 1.20.0
+ *
+ * @since 0.10.14
+ */
+public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerEnchantingTableImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryEnchanting(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ENCHANTING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerEnchantingTableImpl extends EnchantmentMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom enchanting table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @since 0.11.0
+ */
+ public ContainerEnchantingTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+
+ super.checkReachable = false;
+
+ updateSlot(0, inputSlots);
+ updateSlot(1, inputSlots);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.inputSlots);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/GrindstoneInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..02f1c311b
--- /dev/null
+++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.GrindstoneMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal grindstone inventory for 1.20.0
+ *
+ * @since 0.10.14
+ */
+public class GrindstoneInventoryImpl extends GrindstoneInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerGrindstoneImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryGrindstone(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.GRINDSTONE;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container grindstone
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerGrindstoneImpl extends GrindstoneMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerGrindstoneImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryGrindstone inventory = new CraftInventoryGrindstone(this.itemsSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/MerchantInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/MerchantInventoryImpl.java
new file mode 100644
index 000000000..42eb7f999
--- /dev/null
+++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/MerchantInventoryImpl.java
@@ -0,0 +1,300 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil;
+import net.minecraft.network.chat.Component;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.entity.npc.ClientSideMerchant;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.MerchantContainer;
+import net.minecraft.world.inventory.MerchantMenu;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.trading.Merchant;
+import net.minecraft.world.item.trading.MerchantOffer;
+import net.minecraft.world.item.trading.MerchantOffers;
+import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MerchantRecipe;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal merchant inventory for 1.20.0
+ *
+ * @since 0.10.14
+ */
+public class MerchantInventoryImpl extends MerchantInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Merchant merchant = new ClientSideMerchant(null);
+
+ MerchantContainer container = new InventoryViewProvider(merchant) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull net.minecraft.world.entity.player.Player player
+ ) {
+ return new ContainerMerchantImpl(containerId, player, this, merchant);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryMerchant(merchant, container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.MERCHANT;
+ }
+
+ @Override
+ public MerchantContainer getInventory() {
+ return container;
+ }
+ };
+ }
+
+ @Override
+ public void sendMerchantOffers(@NotNull Player player,
+ @NotNull List extends Map.Entry extends MerchantRecipe, ? extends Integer>> trades,
+ int level, int experience) {
+ MerchantOffers offers = new MerchantOffers();
+
+ for (Map.Entry extends MerchantRecipe, ? extends Integer> entry : trades) {
+ MerchantRecipe recipe = entry.getKey();
+ List ingredients = recipe.getIngredients();
+
+ if (ingredients.size() < 1) {
+ throw new IllegalStateException("Merchant recipe has no ingredients");
+ }
+
+ ItemStack itemA = ingredients.get(0);
+ ItemStack itemB = null;
+
+ if (ingredients.size() >= 2) {
+ itemB = ingredients.get(1);
+ }
+
+ net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA);
+ net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY;
+ net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult());
+
+ if (itemB != null) {
+ nmsItemB = CraftItemStack.asNMSCopy(itemB);
+ }
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier
+ );
+ merchantOffer.setSpecialPriceDiff(entry.getValue());
+
+ offers.add(merchantOffer);
+ }
+
+ ServerPlayer serverPlayer = getServerPlayer(player);
+ int containerId = getContainerId(serverPlayer);
+
+ serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false);
+ }
+
+ /**
+ * Gets the server player associated to this player
+ *
+ * @param player the player to get the server player from
+ * @return the server player
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(pure = true)
+ private ServerPlayer getServerPlayer(@NotNull Player player) {
+ return ((CraftPlayer) player).getHandle();
+ }
+
+ /**
+ * Gets the containerId id for the inventory view the player currently has open
+ *
+ * @param nmsPlayer the player to get the containerId id for
+ * @return the containerId id
+ * @since 0.10.14
+ */
+ @Contract(pure = true)
+ private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) {
+ return nmsPlayer.containerMenu.containerId;
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends MerchantContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public InventoryViewProvider(@NotNull Merchant merchant) {
+ super(merchant);
+ }
+ }
+
+ /**
+ * A custom container merchant
+ *
+ * @since 0.11.0
+ */
+ private static class ContainerMerchantImpl extends MerchantMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final MerchantContainer container;
+
+ /**
+ * The merchant.
+ */
+ @NotNull
+ private final Merchant merchant;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param container the items slots
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public ContainerMerchantImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull MerchantContainer container,
+ @NotNull Merchant merchant
+ ) {
+ super(containerId, player.getInventory(), merchant);
+
+ this.humanEntity = player.getBukkitEntity();
+ this.container = container;
+ this.merchant = merchant;
+
+ super.checkReachable = false;
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+
+ Slot slot = super.slots.get(2);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y) {
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPickup(@Nullable net.minecraft.world.entity.player.Player player) {
+ return false;
+ }
+
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPlace(@Nullable net.minecraft.world.item.ItemStack itemStack) {
+ return false;
+ }
+ };
+ newSlot.index = slot.index;
+
+ super.slots.set(2, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryMerchant inventory = new CraftInventoryMerchant(this.merchant, this.container);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable net.minecraft.world.entity.player.Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable net.minecraft.world.entity.player.Player player, @Nullable Container container) {}
+
+ @Override
+ public void setSelectionHint(int i) {}
+
+ @Override
+ public void tryMoveItems(int i) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/SmithingTableInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..a5b1302cf
--- /dev/null
+++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/SmithingTableInventoryImpl.java
@@ -0,0 +1,217 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.ResultContainer;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.inventory.SmithingMenu;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal smithing table inventory for 1.20.0. This is only available for Minecraft 1.20 and higher.
+ *
+ * @since 0.10.14
+ */
+public class SmithingTableInventoryImpl extends SmithingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ ResultContainer resultSlot = new ResultContainer();
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerSmithingTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventorySmithing(null, container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.SMITHING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(3);
+ }
+ }
+
+ /**
+ * A custom container smithing table
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerSmithingTableImpl extends SmithingMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final ResultContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom smithing table container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerSmithingTableImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull ResultContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ updateSlot(3, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventory inventory = new CraftInventorySmithing(
+ this.access.getLocation(),
+ this.itemsSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {}
+
+ @Override
+ protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) {
+ return true;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/StonecutterInventoryImpl.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..c913ba35c
--- /dev/null
+++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/StonecutterInventoryImpl.java
@@ -0,0 +1,200 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_0.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.*;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal stonecutter inventory for 1.20.0
+ *
+ * @since 0.10.14
+ */
+public class StonecutterInventoryImpl extends StonecutterInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerStonecutterImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryStonecutter(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.STONECUTTER;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerStonecutterImpl extends StonecutterMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom stonecutter container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerStonecutterImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer inputSlot,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlot = inputSlot;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlot, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryStonecutter inventory = new CraftInventoryStonecutter(this.inputSlot, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Contract(value = "_, _ -> false", pure = true)
+ @Override
+ public boolean clickMenuButton(@Nullable net.minecraft.world.entity.player.Player player, int index) {
+ return false;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/CustomInventoryUtil.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..5050a7885
--- /dev/null
+++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_0.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A utility class for custom inventories
+ *
+ * @since 0.10.14
+ */
+public final class CustomInventoryUtil {
+
+ /**
+ * A private constructor to prevent construction.
+ */
+ private CustomInventoryUtil() {}
+
+ /**
+ * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items
+ * were specified, this returns an empty list.
+ *
+ * @param items the items to convert
+ * @return a list of converted items
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) {
+ NonNullList nmsItems = NonNullList.create();
+
+ for (org.bukkit.inventory.ItemStack item : items) {
+ nmsItems.add(CraftItemStack.asNMSCopy(item));
+ }
+
+ return nmsItems;
+ }
+}
diff --git a/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/TextHolderUtil.java b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/TextHolderUtil.java
new file mode 100644
index 000000000..ef64afcc5
--- /dev/null
+++ b/nms/1_20_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_0/util/TextHolderUtil.java
@@ -0,0 +1,65 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_0.util;
+
+import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * A utility class for adding {@link TextHolder} support.
+ *
+ * @since 0.10.14
+ */
+public final class TextHolderUtil {
+
+ private TextHolderUtil() {
+ //private constructor to prevent construction
+ }
+
+ /**
+ * Converts the specified value to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static Component toComponent(@NotNull TextHolder holder) {
+ if (holder instanceof StringHolder) {
+ return toComponent((StringHolder) holder);
+ } else {
+ return toComponent((ComponentHolder) holder);
+ }
+ }
+
+ /**
+ * Converts the specified legacy string holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull StringHolder holder) {
+ return Component.literal(holder.asLegacyString());
+ }
+
+ /**
+ * Converts the specified Adventure component holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull ComponentHolder holder) {
+ return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson()));
+ }
+}
diff --git a/nms/1_20_1/pom.xml b/nms/1_20_1/pom.xml
new file mode 100644
index 000000000..cd705a763
--- /dev/null
+++ b/nms/1_20_1/pom.xml
@@ -0,0 +1,58 @@
+
+
+ 4.0.0
+
+ com.github.stefvanschie.inventoryframework
+ IF-parent
+ 0.12.0
+ ../../pom.xml
+
+
+ 1_20_1
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ ca.bkaw
+ paper-nms
+ 1.20.1-SNAPSHOT
+ provided
+
+
+
+
+
+
+ ca.bkaw
+ paper-nms-maven-plugin
+ 1.4.10
+
+
+ process-classes
+
+ remap
+
+
+
+
+
+
+
+
+
+ bytecode.space
+ https://repo.bytecode.space/repository/maven-public/
+
+
+
\ No newline at end of file
diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/AnvilInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/AnvilInventoryImpl.java
new file mode 100644
index 000000000..bcae7b1c0
--- /dev/null
+++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/AnvilInventoryImpl.java
@@ -0,0 +1,229 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.AnvilMenu;
+import net.minecraft.world.inventory.ClickType;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal anvil inventory for 1.20.1
+ *
+ * @since 0.10.14
+ */
+public class AnvilInventoryImpl extends AnvilInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer inputSlots = new SimpleContainer(2);
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ return new CraftInventoryAnvil(null, inputSlots, resultSlot, null) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ANVIL;
+ }
+
+ @Override
+ public Container getInventory() {
+ return new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerAnvilImpl(containerId, player, inputSlots, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {}
+
+ /**
+ * A custom container anvil for responding to item renaming
+ *
+ * @since 0.10.14
+ */
+ private class ContainerAnvilImpl extends AnvilMenu {
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom anvil container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerAnvilImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ this.checkReachable = false;
+ this.cost.set(AnvilInventoryImpl.super.cost);
+
+ CompoundContainer compoundContainer = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, compoundContainer);
+ updateSlot(1, compoundContainer);
+ updateSlot(2, compoundContainer);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(
+ this.access.getLocation(),
+ this.inputSlots,
+ this.resultSlot,
+ this
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void broadcastChanges() {
+ if (super.cost.checkAndClearUpdateFlag()) {
+ broadcastFullState();
+ } else {
+ for (int index = 0; index < super.slots.size(); index++) {
+ if (!ItemStack.matches(super.remoteSlots.get(index), super.slots.get(index).getItem())) {
+ broadcastFullState();
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean setItemName(@Nullable String name) {
+ name = name == null ? "" : name;
+
+ /* Only update if the name is actually different. This may be called even if the name is not different,
+ particularly when putting an item in the first slot. */
+ if (!name.equals(AnvilInventoryImpl.super.observableText.get())) {
+ AnvilInventoryImpl.super.observableText.set(name);
+ }
+
+ //the client predicts the output result, so we broadcast the state again to override it
+ broadcastFullState();
+ return true; //no idea what this is for
+ }
+
+ @Override
+ public void slotsChanged(@NotNull Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, @NotNull ClickType clickType, @NotNull Player player) {
+ super.clicked(index, dragData, clickType, player);
+
+ //client predicts first slot, so send data to override
+ broadcastFullState();
+ }
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ public void removed(@NotNull Player nmsPlayer) {}
+
+ @Override
+ protected void clearContainer(@NotNull Player player, @NotNull Container inventory) {}
+
+ @Override
+ protected void onTake(@NotNull Player player, @NotNull ItemStack stack) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/BeaconInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/BeaconInventoryImpl.java
new file mode 100644
index 000000000..c459d5954
--- /dev/null
+++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/BeaconInventoryImpl.java
@@ -0,0 +1,161 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.BeaconMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.SimpleContainerData;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal beacon inventory for 1.20.1
+ *
+ * @since 0.10.14
+ */
+public class BeaconInventoryImpl extends BeaconInventory {
+
+ @NotNull
+ @Override
+ public Inventory createInventory() {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerBeaconImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return Component.literal("Beacon");
+ }
+ };
+
+ container.setMaxStackSize(1); //client limitation
+
+ return new CraftInventoryBeacon(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.BEACON;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with one slot.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container beacon
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerBeaconImpl extends BeaconMenu {
+
+ /**
+ * The player viewing this menu.
+ */
+ @NotNull
+ private final Player player;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom beacon container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @since 0.11.0
+ */
+ public ContainerBeaconImpl(int containerId, @NotNull Player player, @NotNull SimpleContainer inputSlot) {
+ super(containerId, player.getInventory(), new SimpleContainerData(3),
+ ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.player = player;
+ this.inputSlot = inputSlot;
+
+ super.checkReachable = false;
+
+ Slot slot = super.slots.get(0);
+
+ Slot newSlot = new Slot(inputSlot, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(0, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryBeacon inventory = new CraftInventoryBeacon(this.inputSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ }
+}
diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/CartographyTableInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..d853bd474
--- /dev/null
+++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/CartographyTableInventoryImpl.java
@@ -0,0 +1,196 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.CartographyTableMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal cartography table inventory for 1.20.1
+ *
+ * @since 0.10.14
+ */
+public class CartographyTableInventoryImpl extends CartographyTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerCartographyTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryCartography(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.CARTOGRAPHY;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container cartography table
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerCartographyTableImpl extends CartographyTableMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom cartography table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerCartographyTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryCartography inventory = new CraftInventoryCartography(this.inputSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+
+ }
+}
diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/EnchantingTableInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..7c4f91890
--- /dev/null
+++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/EnchantingTableInventoryImpl.java
@@ -0,0 +1,180 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.EnchantmentMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal enchanting table inventory for 1.20.1
+ *
+ * @since 0.10.14
+ */
+public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerEnchantingTableImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryEnchanting(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ENCHANTING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerEnchantingTableImpl extends EnchantmentMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom enchanting table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @since 0.11.0
+ */
+ public ContainerEnchantingTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+
+ super.checkReachable = false;
+
+ updateSlot(0, inputSlots);
+ updateSlot(1, inputSlots);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.inputSlots);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/GrindstoneInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..3ef8e6fe9
--- /dev/null
+++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.GrindstoneMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal grindstone inventory for 1.20.1
+ *
+ * @since 0.10.14
+ */
+public class GrindstoneInventoryImpl extends GrindstoneInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerGrindstoneImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryGrindstone(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.GRINDSTONE;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container grindstone
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerGrindstoneImpl extends GrindstoneMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerGrindstoneImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryGrindstone inventory = new CraftInventoryGrindstone(this.itemsSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/MerchantInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/MerchantInventoryImpl.java
new file mode 100644
index 000000000..431ac5052
--- /dev/null
+++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/MerchantInventoryImpl.java
@@ -0,0 +1,300 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil;
+import net.minecraft.network.chat.Component;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.entity.npc.ClientSideMerchant;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.MerchantContainer;
+import net.minecraft.world.inventory.MerchantMenu;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.trading.Merchant;
+import net.minecraft.world.item.trading.MerchantOffer;
+import net.minecraft.world.item.trading.MerchantOffers;
+import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MerchantRecipe;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal merchant inventory for 1.20.1
+ *
+ * @since 0.10.14
+ */
+public class MerchantInventoryImpl extends MerchantInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Merchant merchant = new ClientSideMerchant(null);
+
+ MerchantContainer container = new InventoryViewProvider(merchant) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull net.minecraft.world.entity.player.Player player
+ ) {
+ return new ContainerMerchantImpl(containerId, player, this, merchant);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryMerchant(merchant, container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.MERCHANT;
+ }
+
+ @Override
+ public MerchantContainer getInventory() {
+ return container;
+ }
+ };
+ }
+
+ @Override
+ public void sendMerchantOffers(@NotNull Player player,
+ @NotNull List extends Map.Entry extends MerchantRecipe, ? extends Integer>> trades,
+ int level, int experience) {
+ MerchantOffers offers = new MerchantOffers();
+
+ for (Map.Entry extends MerchantRecipe, ? extends Integer> entry : trades) {
+ MerchantRecipe recipe = entry.getKey();
+ List ingredients = recipe.getIngredients();
+
+ if (ingredients.size() < 1) {
+ throw new IllegalStateException("Merchant recipe has no ingredients");
+ }
+
+ ItemStack itemA = ingredients.get(0);
+ ItemStack itemB = null;
+
+ if (ingredients.size() >= 2) {
+ itemB = ingredients.get(1);
+ }
+
+ net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA);
+ net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY;
+ net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult());
+
+ if (itemB != null) {
+ nmsItemB = CraftItemStack.asNMSCopy(itemB);
+ }
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier
+ );
+ merchantOffer.setSpecialPriceDiff(entry.getValue());
+
+ offers.add(merchantOffer);
+ }
+
+ ServerPlayer serverPlayer = getServerPlayer(player);
+ int containerId = getContainerId(serverPlayer);
+
+ serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false);
+ }
+
+ /**
+ * Gets the server player associated to this player
+ *
+ * @param player the player to get the server player from
+ * @return the server player
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(pure = true)
+ private ServerPlayer getServerPlayer(@NotNull Player player) {
+ return ((CraftPlayer) player).getHandle();
+ }
+
+ /**
+ * Gets the containerId id for the inventory view the player currently has open
+ *
+ * @param nmsPlayer the player to get the containerId id for
+ * @return the containerId id
+ * @since 0.10.14
+ */
+ @Contract(pure = true)
+ private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) {
+ return nmsPlayer.containerMenu.containerId;
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends MerchantContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public InventoryViewProvider(@NotNull Merchant merchant) {
+ super(merchant);
+ }
+ }
+
+ /**
+ * A custom container merchant
+ *
+ * @since 0.11.0
+ */
+ private static class ContainerMerchantImpl extends MerchantMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final MerchantContainer container;
+
+ /**
+ * The merchant.
+ */
+ @NotNull
+ private final Merchant merchant;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param container the items slots
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public ContainerMerchantImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull MerchantContainer container,
+ @NotNull Merchant merchant
+ ) {
+ super(containerId, player.getInventory(), merchant);
+
+ this.humanEntity = player.getBukkitEntity();
+ this.container = container;
+ this.merchant = merchant;
+
+ super.checkReachable = false;
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+
+ Slot slot = super.slots.get(2);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y) {
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPickup(@Nullable net.minecraft.world.entity.player.Player player) {
+ return false;
+ }
+
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPlace(@Nullable net.minecraft.world.item.ItemStack itemStack) {
+ return false;
+ }
+ };
+ newSlot.index = slot.index;
+
+ super.slots.set(2, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryMerchant inventory = new CraftInventoryMerchant(this.merchant, this.container);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable net.minecraft.world.entity.player.Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable net.minecraft.world.entity.player.Player player, @Nullable Container container) {}
+
+ @Override
+ public void setSelectionHint(int i) {}
+
+ @Override
+ public void tryMoveItems(int i) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/SmithingTableInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..ab26b15d8
--- /dev/null
+++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/SmithingTableInventoryImpl.java
@@ -0,0 +1,217 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.ResultContainer;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.inventory.SmithingMenu;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal smithing table inventory for 1.20.1. This is only available for Minecraft 1.20 and higher.
+ *
+ * @since 0.10.14
+ */
+public class SmithingTableInventoryImpl extends SmithingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ ResultContainer resultSlot = new ResultContainer();
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerSmithingTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventorySmithing(null, container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.SMITHING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(3);
+ }
+ }
+
+ /**
+ * A custom container smithing table
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerSmithingTableImpl extends SmithingMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final ResultContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom smithing table container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerSmithingTableImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull ResultContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ updateSlot(3, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventory inventory = new CraftInventorySmithing(
+ this.access.getLocation(),
+ this.itemsSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {}
+
+ @Override
+ protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) {
+ return true;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/StonecutterInventoryImpl.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..55fd065bc
--- /dev/null
+++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/StonecutterInventoryImpl.java
@@ -0,0 +1,200 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_1.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.*;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal stonecutter inventory for 1.20.1
+ *
+ * @since 0.10.14
+ */
+public class StonecutterInventoryImpl extends StonecutterInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerStonecutterImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryStonecutter(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.STONECUTTER;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.14
+ */
+ private static class ContainerStonecutterImpl extends StonecutterMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom stonecutter container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerStonecutterImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer inputSlot,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlot = inputSlot;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlot, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryStonecutter inventory = new CraftInventoryStonecutter(this.inputSlot, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Contract(value = "_, _ -> false", pure = true)
+ @Override
+ public boolean clickMenuButton(@Nullable net.minecraft.world.entity.player.Player player, int index) {
+ return false;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/CustomInventoryUtil.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..ed08ff8f4
--- /dev/null
+++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_1.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A utility class for custom inventories
+ *
+ * @since 0.10.14
+ */
+public final class CustomInventoryUtil {
+
+ /**
+ * A private constructor to prevent construction.
+ */
+ private CustomInventoryUtil() {}
+
+ /**
+ * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items
+ * were specified, this returns an empty list.
+ *
+ * @param items the items to convert
+ * @return a list of converted items
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) {
+ NonNullList nmsItems = NonNullList.create();
+
+ for (org.bukkit.inventory.ItemStack item : items) {
+ nmsItems.add(CraftItemStack.asNMSCopy(item));
+ }
+
+ return nmsItems;
+ }
+}
diff --git a/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/TextHolderUtil.java b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/TextHolderUtil.java
new file mode 100644
index 000000000..3438bb279
--- /dev/null
+++ b/nms/1_20_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_1/util/TextHolderUtil.java
@@ -0,0 +1,65 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_1.util;
+
+import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * A utility class for adding {@link TextHolder} support.
+ *
+ * @since 0.10.14
+ */
+public final class TextHolderUtil {
+
+ private TextHolderUtil() {
+ //private constructor to prevent construction
+ }
+
+ /**
+ * Converts the specified value to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static Component toComponent(@NotNull TextHolder holder) {
+ if (holder instanceof StringHolder) {
+ return toComponent((StringHolder) holder);
+ } else {
+ return toComponent((ComponentHolder) holder);
+ }
+ }
+
+ /**
+ * Converts the specified legacy string holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull StringHolder holder) {
+ return Component.literal(holder.asLegacyString());
+ }
+
+ /**
+ * Converts the specified Adventure component holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull ComponentHolder holder) {
+ return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson()));
+ }
+}
diff --git a/nms/1_20_2/pom.xml b/nms/1_20_2/pom.xml
new file mode 100644
index 000000000..09538f0e3
--- /dev/null
+++ b/nms/1_20_2/pom.xml
@@ -0,0 +1,59 @@
+
+
+ 4.0.0
+
+ com.github.stefvanschie.inventoryframework
+ IF-parent
+ 0.12.0
+ ../../pom.xml
+
+
+ 1_20_2
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ ca.bkaw
+ paper-nms
+ 1.20.2-SNAPSHOT
+ provided
+
+
+
+
+
+
+ ca.bkaw
+ paper-nms-maven-plugin
+ 1.4.10
+
+
+ process-classes
+
+ remap
+
+
+
+
+
+
+
+
+
+ bytecode.space
+ https://repo.bytecode.space/repository/maven-public/
+
+
+
+
\ No newline at end of file
diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/AnvilInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/AnvilInventoryImpl.java
new file mode 100644
index 000000000..55b63dc95
--- /dev/null
+++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/AnvilInventoryImpl.java
@@ -0,0 +1,229 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.AnvilMenu;
+import net.minecraft.world.inventory.ClickType;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal anvil inventory for 1.20.2
+ *
+ * @since 0.10.12
+ */
+public class AnvilInventoryImpl extends AnvilInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer inputSlots = new SimpleContainer(2);
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ return new CraftInventoryAnvil(null, inputSlots, resultSlot, null) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ANVIL;
+ }
+
+ @Override
+ public Container getInventory() {
+ return new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerAnvilImpl(containerId, player, inputSlots, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {}
+
+ /**
+ * A custom container anvil for responding to item renaming
+ *
+ * @since 0.10.12
+ */
+ private class ContainerAnvilImpl extends AnvilMenu {
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom anvil container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerAnvilImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ this.checkReachable = false;
+ this.cost.set(AnvilInventoryImpl.super.cost);
+
+ CompoundContainer compoundContainer = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, compoundContainer);
+ updateSlot(1, compoundContainer);
+ updateSlot(2, compoundContainer);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(
+ this.access.getLocation(),
+ this.inputSlots,
+ this.resultSlot,
+ this
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void broadcastChanges() {
+ if (super.cost.checkAndClearUpdateFlag()) {
+ broadcastFullState();
+ } else {
+ for (int index = 0; index < super.slots.size(); index++) {
+ if (!ItemStack.matches(super.remoteSlots.get(index), super.slots.get(index).getItem())) {
+ broadcastFullState();
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean setItemName(@Nullable String name) {
+ name = name == null ? "" : name;
+
+ /* Only update if the name is actually different. This may be called even if the name is not different,
+ particularly when putting an item in the first slot. */
+ if (!name.equals(AnvilInventoryImpl.super.observableText.get())) {
+ AnvilInventoryImpl.super.observableText.set(name);
+ }
+
+ //the client predicts the output result, so we broadcast the state again to override it
+ broadcastFullState();
+ return true; //no idea what this is for
+ }
+
+ @Override
+ public void slotsChanged(@NotNull Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, @NotNull ClickType clickType, @NotNull net.minecraft.world.entity.player.Player player) {
+ super.clicked(index, dragData, clickType, player);
+
+ //client predicts first slot, so send data to override
+ broadcastFullState();
+ }
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ public void removed(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Override
+ protected void clearContainer(@NotNull net.minecraft.world.entity.player.Player player, @NotNull Container inventory) {}
+
+ @Override
+ protected void onTake(@NotNull net.minecraft.world.entity.player.Player player, @NotNull ItemStack stack) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/BeaconInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/BeaconInventoryImpl.java
new file mode 100644
index 000000000..2bac23c86
--- /dev/null
+++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/BeaconInventoryImpl.java
@@ -0,0 +1,161 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.BeaconMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.SimpleContainerData;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal beacon inventory for 1.20.2
+ *
+ * @since 0.10.12
+ */
+public class BeaconInventoryImpl extends BeaconInventory {
+
+ @NotNull
+ @Override
+ public Inventory createInventory() {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerBeaconImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return Component.literal("Beacon");
+ }
+ };
+
+ container.setMaxStackSize(1); //client limitation
+
+ return new CraftInventoryBeacon(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.BEACON;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with one slot.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container beacon
+ *
+ * @since 0.10.12
+ */
+ private static class ContainerBeaconImpl extends BeaconMenu {
+
+ /**
+ * The player viewing this menu.
+ */
+ @NotNull
+ private final Player player;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom beacon container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @since 0.11.0
+ */
+ public ContainerBeaconImpl(int containerId, @NotNull Player player, @NotNull SimpleContainer inputSlot) {
+ super(containerId, player.getInventory(), new SimpleContainerData(3),
+ ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.player = player;
+ this.inputSlot = inputSlot;
+
+ super.checkReachable = false;
+
+ Slot slot = super.slots.get(0);
+
+ Slot newSlot = new Slot(inputSlot, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(0, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryBeacon inventory = new CraftInventoryBeacon(this.inputSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ }
+}
diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/CartographyTableInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..c5cb9074e
--- /dev/null
+++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/CartographyTableInventoryImpl.java
@@ -0,0 +1,196 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.CartographyTableMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal cartography table inventory for 1.20.2
+ *
+ * @since 0.10.12
+ */
+public class CartographyTableInventoryImpl extends CartographyTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerCartographyTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryCartography(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.CARTOGRAPHY;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container cartography table
+ *
+ * @since 0.10.12
+ */
+ private static class ContainerCartographyTableImpl extends CartographyTableMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom cartography table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerCartographyTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryCartography inventory = new CraftInventoryCartography(this.inputSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+
+ }
+}
diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/EnchantingTableInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..03cfd35a5
--- /dev/null
+++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/EnchantingTableInventoryImpl.java
@@ -0,0 +1,180 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.EnchantmentMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal enchanting table inventory for 1.20.2
+ *
+ * @since 0.10.12
+ */
+public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerEnchantingTableImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryEnchanting(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ENCHANTING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.12
+ */
+ private static class ContainerEnchantingTableImpl extends EnchantmentMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom enchanting table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @since 0.11.0
+ */
+ public ContainerEnchantingTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+
+ super.checkReachable = false;
+
+ updateSlot(0, inputSlots);
+ updateSlot(1, inputSlots);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.inputSlots);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/GrindstoneInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..71f44aa6d
--- /dev/null
+++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.GrindstoneMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal grindstone inventory for 1.20.2
+ *
+ * @since 0.10.12
+ */
+public class GrindstoneInventoryImpl extends GrindstoneInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerGrindstoneImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryGrindstone(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.GRINDSTONE;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container grindstone
+ *
+ * @since 0.10.12
+ */
+ private static class ContainerGrindstoneImpl extends GrindstoneMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerGrindstoneImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryGrindstone inventory = new CraftInventoryGrindstone(this.itemsSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/MerchantInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/MerchantInventoryImpl.java
new file mode 100644
index 000000000..93549473e
--- /dev/null
+++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/MerchantInventoryImpl.java
@@ -0,0 +1,300 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil;
+import net.minecraft.network.chat.Component;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.entity.npc.ClientSideMerchant;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.MerchantContainer;
+import net.minecraft.world.inventory.MerchantMenu;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.trading.Merchant;
+import net.minecraft.world.item.trading.MerchantOffer;
+import net.minecraft.world.item.trading.MerchantOffers;
+import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MerchantRecipe;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal merchant inventory for 1.20.2
+ *
+ * @since 0.10.12
+ */
+public class MerchantInventoryImpl extends MerchantInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Merchant merchant = new ClientSideMerchant(null);
+
+ MerchantContainer container = new InventoryViewProvider(merchant) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull net.minecraft.world.entity.player.Player player
+ ) {
+ return new ContainerMerchantImpl(containerId, player, this, merchant);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryMerchant(merchant, container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.MERCHANT;
+ }
+
+ @Override
+ public MerchantContainer getInventory() {
+ return container;
+ }
+ };
+ }
+
+ @Override
+ public void sendMerchantOffers(@NotNull Player player,
+ @NotNull List extends Map.Entry extends MerchantRecipe, ? extends Integer>> trades,
+ int level, int experience) {
+ MerchantOffers offers = new MerchantOffers();
+
+ for (Map.Entry extends MerchantRecipe, ? extends Integer> entry : trades) {
+ MerchantRecipe recipe = entry.getKey();
+ List ingredients = recipe.getIngredients();
+
+ if (ingredients.size() < 1) {
+ throw new IllegalStateException("Merchant recipe has no ingredients");
+ }
+
+ ItemStack itemA = ingredients.get(0);
+ ItemStack itemB = null;
+
+ if (ingredients.size() >= 2) {
+ itemB = ingredients.get(1);
+ }
+
+ net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA);
+ net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY;
+ net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult());
+
+ if (itemB != null) {
+ nmsItemB = CraftItemStack.asNMSCopy(itemB);
+ }
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier
+ );
+ merchantOffer.setSpecialPriceDiff(entry.getValue());
+
+ offers.add(merchantOffer);
+ }
+
+ ServerPlayer serverPlayer = getServerPlayer(player);
+ int containerId = getContainerId(serverPlayer);
+
+ serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false);
+ }
+
+ /**
+ * Gets the server player associated to this player
+ *
+ * @param player the player to get the server player from
+ * @return the server player
+ * @since 0.10.12
+ */
+ @NotNull
+ @Contract(pure = true)
+ private ServerPlayer getServerPlayer(@NotNull Player player) {
+ return ((CraftPlayer) player).getHandle();
+ }
+
+ /**
+ * Gets the containerId id for the inventory view the player currently has open
+ *
+ * @param nmsPlayer the player to get the containerId id for
+ * @return the containerId id
+ * @since 0.10.12
+ */
+ @Contract(pure = true)
+ private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) {
+ return nmsPlayer.containerMenu.containerId;
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends MerchantContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public InventoryViewProvider(@NotNull Merchant merchant) {
+ super(merchant);
+ }
+ }
+
+ /**
+ * A custom container merchant
+ *
+ * @since 0.11.0
+ */
+ private static class ContainerMerchantImpl extends MerchantMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final MerchantContainer container;
+
+ /**
+ * The merchant.
+ */
+ @NotNull
+ private final Merchant merchant;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param container the items slots
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public ContainerMerchantImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull MerchantContainer container,
+ @NotNull Merchant merchant
+ ) {
+ super(containerId, player.getInventory(), merchant);
+
+ this.humanEntity = player.getBukkitEntity();
+ this.container = container;
+ this.merchant = merchant;
+
+ super.checkReachable = false;
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+
+ Slot slot = super.slots.get(2);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y) {
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPickup(@Nullable net.minecraft.world.entity.player.Player player) {
+ return false;
+ }
+
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPlace(@Nullable net.minecraft.world.item.ItemStack itemStack) {
+ return false;
+ }
+ };
+ newSlot.index = slot.index;
+
+ super.slots.set(2, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryMerchant inventory = new CraftInventoryMerchant(this.merchant, this.container);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable net.minecraft.world.entity.player.Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable net.minecraft.world.entity.player.Player player, @Nullable Container container) {}
+
+ @Override
+ public void setSelectionHint(int i) {}
+
+ @Override
+ public void tryMoveItems(int i) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/SmithingTableInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..91afd5ea7
--- /dev/null
+++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/SmithingTableInventoryImpl.java
@@ -0,0 +1,217 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.ResultContainer;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.inventory.SmithingMenu;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal smithing table inventory for 1.20.2. This is only available for Minecraft 1.20 and higher.
+ *
+ * @since 0.10.12
+ */
+public class SmithingTableInventoryImpl extends SmithingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ ResultContainer resultSlot = new ResultContainer();
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerSmithingTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventorySmithing(null, container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.SMITHING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(3);
+ }
+ }
+
+ /**
+ * A custom container smithing table
+ *
+ * @since 0.10.12
+ */
+ private static class ContainerSmithingTableImpl extends SmithingMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final ResultContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom smithing table container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerSmithingTableImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull ResultContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ updateSlot(3, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventory inventory = new CraftInventorySmithing(
+ this.access.getLocation(),
+ this.itemsSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {}
+
+ @Override
+ protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) {
+ return true;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/StonecutterInventoryImpl.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..20d2b5a48
--- /dev/null
+++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/StonecutterInventoryImpl.java
@@ -0,0 +1,200 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_2;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_2.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.*;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal stonecutter inventory for 1.20.2
+ *
+ * @since 0.10.12
+ */
+public class StonecutterInventoryImpl extends StonecutterInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerStonecutterImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryStonecutter(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.STONECUTTER;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.12
+ */
+ private static class ContainerStonecutterImpl extends StonecutterMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom stonecutter container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerStonecutterImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer inputSlot,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlot = inputSlot;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlot, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryStonecutter inventory = new CraftInventoryStonecutter(this.inputSlot, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Contract(value = "_, _ -> false", pure = true)
+ @Override
+ public boolean clickMenuButton(@Nullable net.minecraft.world.entity.player.Player player, int index) {
+ return false;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/CustomInventoryUtil.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..66f18dbff
--- /dev/null
+++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_2.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A utility class for custom inventories
+ *
+ * @since 0.10.12
+ */
+public final class CustomInventoryUtil {
+
+ /**
+ * A private constructor to prevent construction.
+ */
+ private CustomInventoryUtil() {}
+
+ /**
+ * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items
+ * were specified, this returns an empty list.
+ *
+ * @param items the items to convert
+ * @return a list of converted items
+ * @since 0.10.12
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) {
+ NonNullList nmsItems = NonNullList.create();
+
+ for (org.bukkit.inventory.ItemStack item : items) {
+ nmsItems.add(CraftItemStack.asNMSCopy(item));
+ }
+
+ return nmsItems;
+ }
+}
diff --git a/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/TextHolderUtil.java b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/TextHolderUtil.java
new file mode 100644
index 000000000..d1bc6fe98
--- /dev/null
+++ b/nms/1_20_2/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_2/util/TextHolderUtil.java
@@ -0,0 +1,65 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_2.util;
+
+import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * A utility class for adding {@link TextHolder} support.
+ *
+ * @since 0.10.12
+ */
+public final class TextHolderUtil {
+
+ private TextHolderUtil() {
+ //private constructor to prevent construction
+ }
+
+ /**
+ * Converts the specified value to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.12
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static Component toComponent(@NotNull TextHolder holder) {
+ if (holder instanceof StringHolder) {
+ return toComponent((StringHolder) holder);
+ } else {
+ return toComponent((ComponentHolder) holder);
+ }
+ }
+
+ /**
+ * Converts the specified legacy string holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.12
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull StringHolder holder) {
+ return Component.literal(holder.asLegacyString());
+ }
+
+ /**
+ * Converts the specified Adventure component holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.12
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull ComponentHolder holder) {
+ return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson()));
+ }
+}
diff --git a/nms/1_20_3-4/pom.xml b/nms/1_20_3-4/pom.xml
new file mode 100644
index 000000000..37d6b0914
--- /dev/null
+++ b/nms/1_20_3-4/pom.xml
@@ -0,0 +1,110 @@
+
+
+ 4.0.0
+
+ com.github.stefvanschie.inventoryframework
+ IF-parent
+ 0.12.0
+ ../../pom.xml
+
+
+ 1_20_3-4
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ ca.bkaw
+ paper-nms
+ 1.20.3-SNAPSHOT
+ provided
+
+
+
+
+
+
+
+ net.kyori
+ adventure-api
+ 4.26.1
+
+
+ net.kyori
+ adventure-key
+ 4.26.1
+
+
+ net.kyori
+ adventure-text-logger-slf4j
+ 4.26.1
+
+
+ net.kyori
+ adventure-text-minimessage
+ 4.25.0
+
+
+ net.kyori
+ adventure-text-serializer-ansi
+ 4.26.1
+
+
+ net.kyori
+ adventure-text-serializer-gson
+ 4.25.0
+
+
+ net.kyori
+ adventure-text-serializer-json
+ 4.26.1
+
+
+ net.kyori
+ adventure-text-serializer-legacy
+ 4.26.1
+
+
+ net.kyori
+ adventure-text-serializer-plain
+ 4.26.1
+
+
+
+
+
+
+
+ ca.bkaw
+ paper-nms-maven-plugin
+ 1.4.10
+
+
+ process-classes
+
+ remap
+
+
+
+
+
+
+
+
+
+ bytecode.space
+ https://repo.bytecode.space/repository/maven-public/
+
+
+
+
\ No newline at end of file
diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/AnvilInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/AnvilInventoryImpl.java
new file mode 100644
index 000000000..a410aa854
--- /dev/null
+++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/AnvilInventoryImpl.java
@@ -0,0 +1,229 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_3;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.AnvilMenu;
+import net.minecraft.world.inventory.ClickType;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal anvil inventory for 1.20.3
+ *
+ * @since 0.10.13
+ */
+public class AnvilInventoryImpl extends AnvilInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer inputSlots = new SimpleContainer(2);
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ return new CraftInventoryAnvil(null, inputSlots, resultSlot, null) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ANVIL;
+ }
+
+ @Override
+ public Container getInventory() {
+ return new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerAnvilImpl(containerId, player, inputSlots, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {}
+
+ /**
+ * A custom container anvil for responding to item renaming
+ *
+ * @since 0.10.13
+ */
+ private class ContainerAnvilImpl extends AnvilMenu {
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom anvil container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerAnvilImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ this.checkReachable = false;
+ this.cost.set(AnvilInventoryImpl.super.cost);
+
+ CompoundContainer compoundContainer = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, compoundContainer);
+ updateSlot(1, compoundContainer);
+ updateSlot(2, compoundContainer);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(
+ this.access.getLocation(),
+ this.inputSlots,
+ this.resultSlot,
+ this
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void broadcastChanges() {
+ if (super.cost.checkAndClearUpdateFlag()) {
+ broadcastFullState();
+ } else {
+ for (int index = 0; index < super.slots.size(); index++) {
+ if (!ItemStack.matches(super.remoteSlots.get(index), super.slots.get(index).getItem())) {
+ broadcastFullState();
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean setItemName(@Nullable String name) {
+ name = name == null ? "" : name;
+
+ /* Only update if the name is actually different. This may be called even if the name is not different,
+ particularly when putting an item in the first slot. */
+ if (!name.equals(AnvilInventoryImpl.super.observableText.get())) {
+ AnvilInventoryImpl.super.observableText.set(name);
+ }
+
+ //the client predicts the output result, so we broadcast the state again to override it
+ broadcastFullState();
+ return true; //no idea what this is for
+ }
+
+ @Override
+ public void slotsChanged(@NotNull Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, @NotNull ClickType clickType, @NotNull Player player) {
+ super.clicked(index, dragData, clickType, player);
+
+ //client predicts first slot, so send data to override
+ broadcastFullState();
+ }
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ public void removed(@NotNull Player nmsPlayer) {}
+
+ @Override
+ protected void clearContainer(@NotNull Player player, @NotNull Container inventory) {}
+
+ @Override
+ protected void onTake(@NotNull Player player, @NotNull ItemStack stack) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/BeaconInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/BeaconInventoryImpl.java
new file mode 100644
index 000000000..472ce0f3f
--- /dev/null
+++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/BeaconInventoryImpl.java
@@ -0,0 +1,161 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_3;
+
+import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.BeaconMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.SimpleContainerData;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal beacon inventory for 1.20.3
+ *
+ * @since 0.10.13
+ */
+public class BeaconInventoryImpl extends BeaconInventory {
+
+ @NotNull
+ @Override
+ public Inventory createInventory() {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerBeaconImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return Component.literal("Beacon");
+ }
+ };
+
+ container.setMaxStackSize(1); //client limitation
+
+ return new CraftInventoryBeacon(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.BEACON;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with one slot.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container beacon
+ *
+ * @since 0.10.13
+ */
+ private static class ContainerBeaconImpl extends BeaconMenu {
+
+ /**
+ * The player viewing this menu.
+ */
+ @NotNull
+ private final Player player;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom beacon container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @since 0.11.0
+ */
+ public ContainerBeaconImpl(int containerId, @NotNull Player player, @NotNull SimpleContainer inputSlot) {
+ super(containerId, player.getInventory(), new SimpleContainerData(3),
+ ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.player = player;
+ this.inputSlot = inputSlot;
+
+ super.checkReachable = false;
+
+ Slot slot = super.slots.get(0);
+
+ Slot newSlot = new Slot(inputSlot, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(0, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryBeacon inventory = new CraftInventoryBeacon(this.inputSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ }
+}
diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/CartographyTableInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..675ca6dec
--- /dev/null
+++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/CartographyTableInventoryImpl.java
@@ -0,0 +1,196 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_3;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.CartographyTableMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal cartography table inventory for 1.20.3
+ *
+ * @since 0.10.13
+ */
+public class CartographyTableInventoryImpl extends CartographyTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerCartographyTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryCartography(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.CARTOGRAPHY;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container cartography table
+ *
+ * @since 0.10.13
+ */
+ private static class ContainerCartographyTableImpl extends CartographyTableMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom cartography table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerCartographyTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryCartography inventory = new CraftInventoryCartography(this.inputSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+
+ }
+}
diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/EnchantingTableInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..429714477
--- /dev/null
+++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/EnchantingTableInventoryImpl.java
@@ -0,0 +1,180 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_3;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.EnchantmentMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal enchanting table inventory for 1.20.3
+ *
+ * @since 0.10.13
+ */
+public class EnchantingTableInventoryImpl extends EnchantingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerEnchantingTableImpl(containerId, player, this);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryEnchanting(container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.ENCHANTING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.13
+ */
+ private static class ContainerEnchantingTableImpl extends EnchantmentMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the input slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlots;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom enchanting table container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlots the input slots
+ * @since 0.11.0
+ */
+ public ContainerEnchantingTableImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer inputSlots
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlots = inputSlots;
+
+ super.checkReachable = false;
+
+ updateSlot(0, inputSlots);
+ updateSlot(1, inputSlots);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.inputSlots);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/GrindstoneInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..eaf2cc0dd
--- /dev/null
+++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_3;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.GrindstoneMenu;
+import net.minecraft.world.inventory.Slot;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal grindstone inventory for 1.20.3
+ *
+ * @since 0.10.13
+ */
+public class GrindstoneInventoryImpl extends GrindstoneInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerGrindstoneImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryGrindstone(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.GRINDSTONE;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container grindstone
+ *
+ * @since 0.10.13
+ */
+ private static class ContainerGrindstoneImpl extends GrindstoneMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerGrindstoneImpl(
+ int containerId,
+ @NotNull Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryGrindstone inventory = new CraftInventoryGrindstone(this.itemsSlots, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable Player player, @Nullable Container container) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/MerchantInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/MerchantInventoryImpl.java
new file mode 100644
index 000000000..c4a2ef53b
--- /dev/null
+++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/MerchantInventoryImpl.java
@@ -0,0 +1,300 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_3;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil;
+import net.minecraft.network.chat.Component;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.entity.npc.ClientSideMerchant;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.MerchantContainer;
+import net.minecraft.world.inventory.MerchantMenu;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.trading.Merchant;
+import net.minecraft.world.item.trading.MerchantOffer;
+import net.minecraft.world.item.trading.MerchantOffers;
+import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MerchantRecipe;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal merchant inventory for 1.20.3
+ *
+ * @since 0.10.13
+ */
+public class MerchantInventoryImpl extends MerchantInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ Merchant merchant = new ClientSideMerchant(null);
+
+ MerchantContainer container = new InventoryViewProvider(merchant) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull net.minecraft.world.entity.player.Player player
+ ) {
+ return new ContainerMerchantImpl(containerId, player, this, merchant);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryMerchant(merchant, container) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.MERCHANT;
+ }
+
+ @Override
+ public MerchantContainer getInventory() {
+ return container;
+ }
+ };
+ }
+
+ @Override
+ public void sendMerchantOffers(@NotNull Player player,
+ @NotNull List extends Map.Entry extends MerchantRecipe, ? extends Integer>> trades,
+ int level, int experience) {
+ MerchantOffers offers = new MerchantOffers();
+
+ for (Map.Entry extends MerchantRecipe, ? extends Integer> entry : trades) {
+ MerchantRecipe recipe = entry.getKey();
+ List ingredients = recipe.getIngredients();
+
+ if (ingredients.size() < 1) {
+ throw new IllegalStateException("Merchant recipe has no ingredients");
+ }
+
+ ItemStack itemA = ingredients.get(0);
+ ItemStack itemB = null;
+
+ if (ingredients.size() >= 2) {
+ itemB = ingredients.get(1);
+ }
+
+ net.minecraft.world.item.ItemStack nmsItemA = CraftItemStack.asNMSCopy(itemA);
+ net.minecraft.world.item.ItemStack nmsItemB = net.minecraft.world.item.ItemStack.EMPTY;
+ net.minecraft.world.item.ItemStack nmsItemResult = CraftItemStack.asNMSCopy(recipe.getResult());
+
+ if (itemB != null) {
+ nmsItemB = CraftItemStack.asNMSCopy(itemB);
+ }
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ nmsItemA, nmsItemB, nmsItemResult, uses, maxUses, exp, multiplier
+ );
+ merchantOffer.setSpecialPriceDiff(entry.getValue());
+
+ offers.add(merchantOffer);
+ }
+
+ ServerPlayer serverPlayer = getServerPlayer(player);
+ int containerId = getContainerId(serverPlayer);
+
+ serverPlayer.sendMerchantOffers(containerId, offers, level, experience, true, false);
+ }
+
+ /**
+ * Gets the server player associated to this player
+ *
+ * @param player the player to get the server player from
+ * @return the server player
+ * @since 0.10.13
+ */
+ @NotNull
+ @Contract(pure = true)
+ private ServerPlayer getServerPlayer(@NotNull Player player) {
+ return ((CraftPlayer) player).getHandle();
+ }
+
+ /**
+ * Gets the containerId id for the inventory view the player currently has open
+ *
+ * @param nmsPlayer the player to get the containerId id for
+ * @return the containerId id
+ * @since 0.10.13
+ */
+ @Contract(pure = true)
+ private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) {
+ return nmsPlayer.containerMenu.containerId;
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends MerchantContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public InventoryViewProvider(@NotNull Merchant merchant) {
+ super(merchant);
+ }
+ }
+
+ /**
+ * A custom container merchant
+ *
+ * @since 0.11.0
+ */
+ private static class ContainerMerchantImpl extends MerchantMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final MerchantContainer container;
+
+ /**
+ * The merchant.
+ */
+ @NotNull
+ private final Merchant merchant;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom grindstone container for the specified player.
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param container the items slots
+ * @param merchant the merchant
+ * @since 0.11.0
+ */
+ public ContainerMerchantImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull MerchantContainer container,
+ @NotNull Merchant merchant
+ ) {
+ super(containerId, player.getInventory(), merchant);
+
+ this.humanEntity = player.getBukkitEntity();
+ this.container = container;
+ this.merchant = merchant;
+
+ super.checkReachable = false;
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+
+ Slot slot = super.slots.get(2);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y) {
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPickup(@Nullable net.minecraft.world.entity.player.Player player) {
+ return false;
+ }
+
+ @Contract(value = "_ -> false", pure = true)
+ @Override
+ public boolean mayPlace(@Nullable net.minecraft.world.item.ItemStack itemStack) {
+ return false;
+ }
+ };
+ newSlot.index = slot.index;
+
+ super.slots.set(2, newSlot);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryMerchant inventory = new CraftInventoryMerchant(this.merchant, this.container);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void slotsChanged(@Nullable Container container) {}
+
+ @Override
+ public void removed(@Nullable net.minecraft.world.entity.player.Player player) {}
+
+ @Override
+ protected void clearContainer(@Nullable net.minecraft.world.entity.player.Player player, @Nullable Container container) {}
+
+ @Override
+ public void setSelectionHint(int i) {}
+
+ @Override
+ public void tryMoveItems(int i) {}
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/SmithingTableInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..e7499c4e3
--- /dev/null
+++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/SmithingTableInventoryImpl.java
@@ -0,0 +1,217 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_3;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.ResultContainer;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.inventory.SmithingMenu;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal smithing table inventory for 1.20.3. This is only available for Minecraft 1.20 and higher.
+ *
+ * @since 0.10.13
+ */
+public class SmithingTableInventoryImpl extends SmithingTableInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ ResultContainer resultSlot = new ResultContainer();
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerSmithingTableImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventorySmithing(null, container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.SMITHING;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(3);
+ }
+ }
+
+ /**
+ * A custom container smithing table
+ *
+ * @since 0.10.13
+ */
+ private static class ContainerSmithingTableImpl extends SmithingMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer itemsSlots;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final ResultContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom smithing table container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param itemsSlots the items slots
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerSmithingTableImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer itemsSlots,
+ @NotNull ResultContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.itemsSlots = itemsSlots;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(itemsSlots, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ updateSlot(2, container);
+ updateSlot(3, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventory inventory = new CraftInventorySmithing(
+ this.access.getLocation(),
+ this.itemsSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Override
+ public void createResult() {}
+
+ @Override
+ protected void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {}
+
+ @Override
+ protected boolean mayPickup(net.minecraft.world.entity.player.Player player, boolean present) {
+ return true;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/StonecutterInventoryImpl.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..2a845182c
--- /dev/null
+++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/StonecutterInventoryImpl.java
@@ -0,0 +1,200 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_3;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_3.util.TextHolderUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.CompoundContainer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.*;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Internal stonecutter inventory for 1.20.3
+ *
+ * @since 0.10.13
+ */
+public class StonecutterInventoryImpl extends StonecutterInventory {
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Inventory createInventory(@NotNull TextHolder title) {
+ SimpleContainer resultSlot = new SimpleContainer(1);
+
+ Container container = new InventoryViewProvider() {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public AbstractContainerMenu createMenu(
+ int containerId,
+ @Nullable net.minecraft.world.entity.player.Inventory inventory,
+ @NotNull Player player
+ ) {
+ return new ContainerStonecutterImpl(containerId, player, this, resultSlot);
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public Component getDisplayName() {
+ return TextHolderUtil.toComponent(title);
+ }
+ };
+
+ return new CraftInventoryStonecutter(container, resultSlot) {
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public InventoryType getType() {
+ return InventoryType.STONECUTTER;
+ }
+
+ @Override
+ public Container getInventory() {
+ return container;
+ }
+ };
+ }
+
+ /**
+ * This is a nice hack to get CraftBukkit to create custom inventories. By providing a container that is also a menu
+ * provider, CraftBukkit will allow us to create a custom menu, rather than picking one of the built-in options.
+ * That way, we can provide a menu with custom behaviour.
+ *
+ * @since 0.11.0
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.0
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container enchanting table
+ *
+ * @since 0.10.13
+ */
+ private static class ContainerStonecutterImpl extends StonecutterMenu {
+
+ /**
+ * The human entity viewing this menu.
+ */
+ @NotNull
+ private final HumanEntity humanEntity;
+
+ /**
+ * The container for the items slots.
+ */
+ @NotNull
+ private final SimpleContainer inputSlot;
+
+ /**
+ * The container for the result slot.
+ */
+ @NotNull
+ private final SimpleContainer resultSlot;
+
+ /**
+ * The corresponding Bukkit view. Will be not null after the first call to {@link #getBukkitView()} and null
+ * prior.
+ */
+ @Nullable
+ private CraftInventoryView bukkitEntity;
+
+ /**
+ * Creates a new custom stonecutter container for the specified player
+ *
+ * @param containerId the container id
+ * @param player the player
+ * @param inputSlot the input slot
+ * @param resultSlot the result slot
+ * @since 0.11.0
+ */
+ public ContainerStonecutterImpl(
+ int containerId,
+ @NotNull net.minecraft.world.entity.player.Player player,
+ @NotNull SimpleContainer inputSlot,
+ @NotNull SimpleContainer resultSlot
+ ) {
+ super(containerId, player.getInventory(), ContainerLevelAccess.create(player.level(), BlockPos.ZERO));
+
+ this.humanEntity = player.getBukkitEntity();
+ this.inputSlot = inputSlot;
+ this.resultSlot = resultSlot;
+
+ super.checkReachable = false;
+
+ CompoundContainer container = new CompoundContainer(inputSlot, resultSlot);
+
+ updateSlot(0, container);
+ updateSlot(1, container);
+ }
+
+ @NotNull
+ @Override
+ public CraftInventoryView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryStonecutter inventory = new CraftInventoryStonecutter(this.inputSlot, this.resultSlot);
+
+ this.bukkitEntity = new CraftInventoryView(this.humanEntity, inventory, this);
+
+ return this.bukkitEntity;
+ }
+
+ @Contract(pure = true, value = "_ -> true")
+ @Override
+ public boolean stillValid(@Nullable net.minecraft.world.entity.player.Player nmsPlayer) {
+ return true;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {}
+
+ @Override
+ public void removed(net.minecraft.world.entity.player.Player nmsPlayer) {}
+
+ @Contract(value = "_, _ -> false", pure = true)
+ @Override
+ public boolean clickMenuButton(@Nullable net.minecraft.world.entity.player.Player player, int index) {
+ return false;
+ }
+
+ /**
+ * Updates the current slot at the specified index to a new slot. The new slot will have the same slot, x, y,
+ * and index as the original. The container of the new slot will be set to the value specified.
+ *
+ * @param slotIndex the slot index to update
+ * @param container the container of the new slot
+ * @since 0.11.0
+ */
+ private void updateSlot(int slotIndex, @NotNull Container container) {
+ Slot slot = super.slots.get(slotIndex);
+
+ Slot newSlot = new Slot(container, slot.slot, slot.x, slot.y);
+ newSlot.index = slot.index;
+
+ super.slots.set(slotIndex, newSlot);
+ }
+ }
+}
diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/CustomInventoryUtil.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..3a6cdb8d1
--- /dev/null
+++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_3.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A utility class for custom inventories
+ *
+ * @since 0.10.13
+ */
+public final class CustomInventoryUtil {
+
+ /**
+ * A private constructor to prevent construction.
+ */
+ private CustomInventoryUtil() {}
+
+ /**
+ * Converts an array of Bukkit items into a non-null list of NMS items. The returned list is modifiable. If no items
+ * were specified, this returns an empty list.
+ *
+ * @param items the items to convert
+ * @return a list of converted items
+ * @since 0.10.13
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static NonNullList convertToNMSItems(@Nullable org.bukkit.inventory.ItemStack @NotNull [] items) {
+ NonNullList nmsItems = NonNullList.create();
+
+ for (org.bukkit.inventory.ItemStack item : items) {
+ nmsItems.add(CraftItemStack.asNMSCopy(item));
+ }
+
+ return nmsItems;
+ }
+}
diff --git a/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/TextHolderUtil.java b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/TextHolderUtil.java
new file mode 100644
index 000000000..f5cbc26ee
--- /dev/null
+++ b/nms/1_20_3-4/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_3/util/TextHolderUtil.java
@@ -0,0 +1,65 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_3.util;
+
+import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+/**
+ * A utility class for adding {@link TextHolder} support.
+ *
+ * @since 0.10.13
+ */
+public final class TextHolderUtil {
+
+ private TextHolderUtil() {
+ //private constructor to prevent construction
+ }
+
+ /**
+ * Converts the specified value to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.13
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static Component toComponent(@NotNull TextHolder holder) {
+ if (holder instanceof StringHolder) {
+ return toComponent((StringHolder) holder);
+ } else {
+ return toComponent((ComponentHolder) holder);
+ }
+ }
+
+ /**
+ * Converts the specified legacy string holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.13
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull StringHolder holder) {
+ return Component.literal(holder.asLegacyString());
+ }
+
+ /**
+ * Converts the specified Adventure component holder to a vanilla component.
+ *
+ * @param holder the value to convert
+ * @return the value as a vanilla component
+ * @since 0.10.13
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull ComponentHolder holder) {
+ return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson()));
+ }
+}
diff --git a/nms/1_20_5/pom.xml b/nms/1_20_5/pom.xml
new file mode 100644
index 000000000..021f5fa53
--- /dev/null
+++ b/nms/1_20_5/pom.xml
@@ -0,0 +1,73 @@
+
+
+ 4.0.0
+
+ com.github.stefvanschie.inventoryframework
+ IF-parent
+ 0.12.0
+ ../../pom.xml
+
+
+ 1_20_5
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ org.spigotmc
+ spigot
+ 1.20.5-R0.1-SNAPSHOT
+ remapped-mojang
+ provided
+
+
+
+
+
+
+ net.md-5
+ specialsource-maven-plugin
+ 2.0.4
+
+