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;
+
+ try {
+ length = Integer.parseInt(element.getAttribute("length"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Length attribute is not an integer", exception);
+ }
- return patternPane;
+ 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;
+ }
+
+ /**
+ * Loads a pattern pane from a given element
+ *
+ * @param instance the instance class
+ * @param element the element
+ * @return the pattern pane
+ * @deprecated this method is no longer used internally and has been superseded by
+ * {@link #load(Object, Element, Plugin)}
+ */
+ @NotNull
+ @Deprecated
+ public static PatternPane load(@NotNull Object instance, @NotNull Element element) {
+ return load(instance, element, JavaPlugin.getProvidingPlugin(PatternPane.class));
}
}
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..88cb5fb6e 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
@@ -4,9 +4,12 @@
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.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,6 +22,12 @@
/**
* 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 {
@@ -27,7 +36,7 @@ public class StaticPane extends Pane implements Flippable, Rotatable {
* the key and the y coordinate is the value.
*/
@NotNull
- private final Map, GuiItem> items;
+ private final Map items;
/**
* The clockwise rotation of this pane in degrees
@@ -39,12 +48,37 @@ 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 slot the slot 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.10.8
+ */
+ public StaticPane(Slot slot, int length, int height, @NotNull Priority priority) {
+ super(slot, length, height, priority);
this.items = new HashMap<>(length * height);
}
+ public StaticPane(int x, int y, int length, int height, @NotNull Priority priority) {
+ this(Slot.fromXY(x, y), length, height, priority);
+ }
+
+ /**
+ * Creates a new static pane.
+ *
+ * @param slot the slot of the pane
+ * @param length the length of the pane
+ * @param height the height of the pane
+ * @since 0.10.8
+ */
+ public StaticPane(Slot slot, int length, int height) {
+ this(slot, length, height, Priority.NORMAL);
+ }
+
public StaticPane(int x, int y, int length, int height) {
this(x, y, length, height, Priority.NORMAL);
}
@@ -53,6 +87,18 @@ public StaticPane(int length, int height) {
this(0, 0, length, height);
}
+ /**
+ * {@inheritDoc}
+ *
+ * If there are multiple items in the same position when displaying the items, either one of those items may be
+ * shown. In particular, there is no guarantee that a specific item will be shown.
+ *
+ * @param inventoryComponent {@inheritDoc}
+ * @param paneOffsetX {@inheritDoc}
+ * @param paneOffsetY {@inheritDoc}
+ * @param maxLength {@inheritDoc}
+ * @param maxHeight {@inheritDoc}
+ */
@Override
public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength,
int maxHeight) {
@@ -60,9 +106,10 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs
int height = Math.min(this.height, maxHeight);
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;
@@ -82,27 +129,41 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs
GuiItem item = entry.getValue();
- int finalRow = getY() + y + paneOffsetY;
- int finalColumn = getX() + x + paneOffsetX;
+ Slot slot = getSlot();
+ int finalRow = slot.getY(maxLength) + y + paneOffsetY;
+ int finalColumn = slot.getX(maxLength) + x + paneOffsetX;
inventoryComponent.setItem(item, finalColumn, finalRow);
});
}
/**
- * 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));
}
+ /**
+ * Adds a gui item at the specific spot in the pane. If the slot is specified in terms of an x and y coordinate pair
+ * and this pane contains another item whose position is specified as such and these positions are equal, the old
+ * item will be overwritten by this item. If the slot is specified in terms of an index and this pane contains
+ * another item whose position is specified as such and these positions are equal, the old item will be overwritten
+ * by this item.
+ *
+ * @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) {
+ this.items.put(slot, item);
+ }
+
/**
* Removes the specified item from the pane
*
@@ -113,6 +174,29 @@ public void removeItem(@NotNull GuiItem item) {
items.values().removeIf(guiItem -> guiItem.equals(item));
}
+ /**
+ * Removes the specified item from the pane. This will only remove items whose slot was specified in terms of an x
+ * and y coordinate pair which matches the coordinate specified.
+ *
+ * @param x the x coordinate of the item to remove
+ * @param y the y coordinate of the item to remove
+ * @since 0.10.0
+ */
+ public void removeItem(int x, int y) {
+ this.items.remove(Slot.fromXY(x, y));
+ }
+
+ /**
+ * Removes the specified item from the pane. This will only remove items whose slot was specified in the same way as
+ * the original slot and whose slot positions match.
+ *
+ * @param slot the slot of the item to remove
+ * @since 0.10.8
+ */
+ public void removeItem(@NotNull Slot slot) {
+ this.items.remove(slot);
+ }
+
@Override
public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent,
@NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength,
@@ -120,10 +204,17 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
int length = Math.min(this.length, maxLength);
int height = Math.min(this.height, maxHeight);
- int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY);
+ Slot paneSlot = getSlot();
+
+ int xPosition = paneSlot.getX(maxLength);
+ int yPosition = paneSlot.getY(maxLength);
+
+ int totalLength = inventoryComponent.getLength();
+
+ int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY);
- int x = adjustedSlot % inventoryComponent.getLength();
- int y = adjustedSlot / inventoryComponent.getLength();
+ int x = adjustedSlot % totalLength;
+ int y = adjustedSlot / totalLength;
//this isn't our item
if (x < 0 || x >= length || y < 0 || y >= height) {
@@ -153,12 +244,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());
+ StaticPane staticPane = new StaticPane(getSlot(), length, height, getPriority());
- for (Map.Entry, GuiItem> entry : items.entrySet()) {
- Map.Entry coordinates = entry.getKey();
-
- 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 +278,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 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 action) {
+ fillWith(itemStack, action, JavaPlugin.getProvidingPlugin(StaticPane.class));
+ }
+
/**
* Fills all empty space in the pane with the given {@code itemStack}
*
@@ -225,12 +328,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 +421,73 @@ 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");
+ }
+
+ if (!element.hasAttribute("height")) {
+ throw new XMLLoadException("Cycle button XML tag does not have the mandatory height attribute");
+ }
+
+ int length;
+ int height;
- Pane.load(staticPane, instance, element);
- Flippable.load(staticPane, element);
- Rotatable.load(staticPane, element);
+ try {
+ length = Integer.parseInt(element.getAttribute("length"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Length attribute is not an integer", exception);
+ }
- if (element.hasAttribute("populate"))
- return staticPane;
+ try {
+ height = Integer.parseInt(element.getAttribute("height"));
+ } catch (NumberFormatException exception) {
+ throw new XMLLoadException("Height attribute is not an integer", exception);
+ }
- NodeList childNodes = element.getChildNodes();
+ StaticPane staticPane = new StaticPane(length, height);
- for (int i = 0; i < childNodes.getLength(); i++) {
- Node item = childNodes.item(i);
+ Pane.load(staticPane, instance, element);
+ Flippable.load(staticPane, element);
+ Rotatable.load(staticPane, element);
- if (item.getNodeType() != Node.ELEMENT_NODE)
- continue;
+ if (element.hasAttribute("populate"))
+ return staticPane;
- Element child = (Element) item;
+ NodeList childNodes = element.getChildNodes();
- staticPane.addItem(Pane.loadItem(instance, child), Integer.parseInt(child.getAttribute("x")),
- Integer.parseInt(child.getAttribute("y")));
- }
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node item = childNodes.item(i);
- return staticPane;
- } catch (NumberFormatException exception) {
- throw new XMLLoadException(exception);
- }
+ if (item.getNodeType() != Node.ELEMENT_NODE)
+ continue;
+
+ Element child = (Element) item;
+
+ staticPane.addItem(Pane.loadItem(instance, child, plugin), Slot.deserialize(child));
+ }
+
+ return staticPane;
}
+
+ /**
+ * Loads an outline pane from a given element
+ *
+ * @param instance the instance class
+ * @param element the element
+ * @return the outline pane
+ * @deprecated this method is no longer used internally and has been superseded by
+ * {@link #load(Object, Element, Plugin)}
+ */
+ @NotNull
+ @Deprecated
+ public static StaticPane load(@NotNull Object instance, @NotNull Element element) {
+ return load(instance, element, JavaPlugin.getProvidingPlugin(StaticPane.class));
+ }
}
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..b7e673eec 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
@@ -5,7 +5,10 @@
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.Slot;
import org.bukkit.event.inventory.InventoryClickEvent;
+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;
@@ -32,10 +35,35 @@ public class CycleButton extends Pane {
*/
private int position = 0;
+ /**
+ * Creates a new cycle button
+ *
+ * @param slot the slot of the button
+ * @param length the length of the button
+ * @param height the height of the button
+ * @param priority the priority of the button
+ * @since 0.10.8
+ */
+ public CycleButton(@NotNull Slot slot, int length, int height, @NotNull Priority priority) {
+ super(slot, length, height, priority);
+ }
+
public CycleButton(int x, int y, int length, int height, @NotNull Priority priority) {
super(x, y, length, height, priority);
}
+ /**
+ * Creates a new cycle button
+ *
+ * @param slot the slot of the button
+ * @param length the length of the button
+ * @param height the height of the button
+ * @since 0.10.8
+ */
+ public CycleButton(@NotNull Slot slot, int length, int height) {
+ super(slot, length, height);
+ }
+
public CycleButton(int x, int y, int length, int height) {
super(x, y, length, height);
}
@@ -51,16 +79,25 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
int length = Math.min(this.length, maxLength);
int height = Math.min(this.height, maxHeight);
- int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY);
+ Slot paneSlot = getSlot();
- int x = adjustedSlot % inventoryComponent.getLength();
- int y = adjustedSlot / inventoryComponent.getLength();
+ int xPosition = paneSlot.getX(maxLength);
+ int yPosition = paneSlot.getY(maxLength);
+
+ int totalLength = inventoryComponent.getLength();
+
+ int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY);
+
+ int x = adjustedSlot % totalLength;
+ int y = adjustedSlot / totalLength;
//this isn't our item
if (x < 0 || x >= length || y < 0 || y >= height) {
return false;
}
+ int previousPosition = position;
+
position++;
if (position == panes.size()) {
@@ -69,7 +106,8 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
callOnClick(event);
- Pane pane = panes.get(position);
+ //use the previous position, since that will have the pane we clicked on
+ Pane pane = panes.get(previousPosition);
pane.click(gui, inventoryComponent, event, slot, paneOffsetX + x, paneOffsetY + y,
length, height);
@@ -81,8 +119,10 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
@Override
public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength,
int maxHeight) {
- int newX = paneOffsetX + x;
- int newY = paneOffsetY + y;
+ Slot slot = getSlot();
+
+ int newX = paneOffsetX + slot.getX(maxLength);
+ int newY = paneOffsetY + slot.getY(maxLength);
int newMaxLength = Math.min(maxLength, length);
int newMaxHeight = Math.min(maxHeight, height);
@@ -94,7 +134,7 @@ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffs
@Contract(pure = true)
@Override
public CycleButton copy() {
- CycleButton cycleButton = new CycleButton(x, y, length, height, getPriority());
+ CycleButton cycleButton = new CycleButton(getSlot(), length, height, getPriority());
for (Pane pane : panes) {
cycleButton.addPane(pane);
@@ -162,19 +202,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,9 +248,25 @@ 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;
}
+
+ /**
+ * Loads a cycle button from a given element
+ *
+ * @param instance the instance class
+ * @param element the element
+ * @return the cycle button
+ * @since 0.5.0
+ * @deprecated this method is no longer used internally and has been superseded by
+ * {@link #load(Object, Element, Plugin)}
+ */
+ @NotNull
+ @Deprecated
+ public static CycleButton load(@NotNull Object instance, @NotNull Element element) {
+ return load(instance, element, JavaPlugin.getProvidingPlugin(CycleButton.class));
+ }
}
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..6e724e534 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
@@ -6,12 +6,17 @@
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.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.function.BiFunction;
+
/**
* A label for displaying text.
*
@@ -31,6 +36,113 @@ public class Label extends OutlinePane {
@NotNull
private String text;
+ /**
+ * The plugin to be sed for creating items
+ */
+ @NotNull
+ private final Plugin plugin;
+
+ /**
+ * Creates a new label
+ *
+ * @param slot the slot
+ * @param length the length
+ * @param height the height
+ * @param priority the priority
+ * @param font the character set
+ * @param plugin the plugin that will be the owner for this label's items
+ * @see #Label(int, int, int, int, Priority, Font)
+ * @since 0.10.8
+ */
+ public Label(@NotNull Slot slot, int length, int height, @NotNull Priority priority, @NotNull Font font,
+ @NotNull Plugin plugin) {
+ super(slot, length, height);
+
+ this.font = font;
+ this.text = "";
+
+ this.plugin = plugin;
+
+ setPriority(priority);
+ }
+
+ /**
+ * 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
+ * @param plugin the plugin that will be the owner for this label's items
+ * @see #Label(int, int, int, int, Priority, Font)
+ * @since 0.10.8
+ */
+ public Label(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Font font,
+ @NotNull Plugin plugin) {
+ this(Slot.fromXY(x, y), length, height, priority, font, plugin);
+ }
+
+ /**
+ * Creates a new label
+ *
+ * @param slot the slot
+ * @param length the length
+ * @param height the height
+ * @param font the character set
+ * @param plugin the plugin that will be the owner for this label's items
+ * @see #Label(int, int, int, int, Font)
+ * @since 0.10.8
+ */
+ public Label(@NotNull Slot slot, int length, int height, @NotNull Font font, @NotNull Plugin plugin) {
+ this(slot, length, height, Priority.NORMAL, font, plugin);
+ }
+
+ /**
+ * 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
+ * @param plugin the plugin that will be the owner for this label's items
+ * @see #Label(int, int, int, int, Font)
+ * @since 0.10.8
+ */
+ public Label(int x, int y, int length, int height, @NotNull Font font, @NotNull Plugin plugin) {
+ this(x, y, length, height, Priority.NORMAL, font, plugin);
+ }
+
+ /**
+ * Creates a new label
+ *
+ * @param length the length
+ * @param height the height
+ * @param font the character set
+ * @param plugin the plugin that will be the owner for this label's items
+ * @see #Label(int, int, Font)
+ * @since 0.10.8
+ */
+ public Label(int length, int height, @NotNull Font font, @NotNull Plugin plugin) {
+ this(0, 0, length, height, font, plugin);
+ }
+
+ /**
+ * Creates a new label
+ *
+ * @param slot the slot
+ * @param length the length
+ * @param height the height
+ * @param priority the priority
+ * @param font the character set
+ * @since 0.10.8
+ */
+ public Label(@NotNull Slot slot, int length, int height, @NotNull Priority priority, @NotNull Font font) {
+ this(slot, length, height, priority, font, JavaPlugin.getProvidingPlugin(Label.class));
+ }
+
/**
* Creates a new label
*
@@ -43,9 +155,20 @@ public class Label extends OutlinePane {
* @since 0.5.0
*/
public Label(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Font font) {
- this(x, y, length, height, font);
+ this(x, y, length, height, priority, font, JavaPlugin.getProvidingPlugin(Label.class));
+ }
- setPriority(priority);
+ /**
+ * Creates a new label
+ *
+ * @param slot the slot
+ * @param length the length
+ * @param height the height
+ * @param font the character set
+ * @since 0.10.8
+ */
+ public Label(@NotNull Slot slot, int length, int height, @NotNull Font font) {
+ this(slot, length, height, Priority.NORMAL, font);
}
/**
@@ -59,10 +182,7 @@ public Label(int x, int y, int length, int height, @NotNull Priority priority, @
* @since 0.5.0
*/
public Label(int x, int y, int length, int height, @NotNull Font font) {
- this(length, height, font);
-
- this.x = x;
- this.y = y;
+ this(x, y, length, height, Priority.NORMAL, font);
}
/**
@@ -74,19 +194,22 @@ public Label(int x, int y, int length, int height, @NotNull Font font) {
* @since 0.5.0
*/
public Label(int length, int height, @NotNull Font font) {
- super(length, height);
-
- this.font = font;
- this.text = "";
+ this(0, 0, length, height, 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,15 +229,26 @@ public void setText(@NotNull String text) {
item = font.getDefaultItem();
}
- addItem(new GuiItem(item));
+ 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);
+ Label label = new Label(getSlot(), length, height, getPriority(), font, this.plugin);
for (GuiItem item : getItems()) {
label.addItem(item.copy());
@@ -174,20 +308,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,7 +349,7 @@ 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);
@@ -217,4 +366,20 @@ public static Label load(@NotNull Object instance, @NotNull Element element) {
return label;
}
+
+ /**
+ * Loads a label from a given element
+ *
+ * @param instance the instance class
+ * @param element the element
+ * @return the percentage bar
+ * @deprecated this method is no longer used internally and has been superseded by
+ * {@link #load(Object, Element, Plugin)}
+ */
+ @NotNull
+ @Contract(pure = true)
+ @Deprecated
+ public static Label load(@NotNull Object instance, @NotNull Element element) {
+ return load(instance, element, JavaPlugin.getProvidingPlugin(Label.class));
+ }
}
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..e0499623d
--- /dev/null
+++ b/IF/src/main/java/com/github/stefvanschie/inventoryframework/pane/component/PagingButtons.java
@@ -0,0 +1,419 @@
+package com.github.stefvanschie.inventoryframework.pane.component;
+
+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.type.util.Gui;
+import com.github.stefvanschie.inventoryframework.pane.PaginatedPane;
+import com.github.stefvanschie.inventoryframework.pane.Pane;
+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 slot the position of this interface
+ * @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.10.14
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(
+ @NotNull Slot slot,
+ int length,
+ @NotNull Priority priority,
+ @NotNull PaginatedPane pages,
+ @NotNull Plugin plugin
+ ) {
+ super(slot, 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 slot the position of this interface
+ * @param length the length of this interface
+ * @param priority the priority of this interface
+ * @param pages the pages to interact with
+ * @since 0.10.14
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(@NotNull Slot slot, int length, @NotNull Priority priority, @NotNull PaginatedPane pages) {
+ this(slot, 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 slot the position of this interface
+ * @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.10.14
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(@NotNull Slot slot, int length, @NotNull PaginatedPane pages, @NotNull Plugin plugin) {
+ this(slot, 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 slot the position of this interface
+ * @param length the length of this interface
+ * @param pages the pages to interact with
+ * @since 0.10.14
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(@NotNull Slot slot, int length, @NotNull PaginatedPane pages) {
+ this(slot, length, Priority.NORMAL, pages);
+ }
+
+ /**
+ * 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.10.14
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(int length, @NotNull Priority priority, @NotNull PaginatedPane pages, @NotNull Plugin plugin) {
+ this(Slot.fromXY(0, 0), length, priority, 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 priority the priority of this interface
+ * @param pages the pages to interact with
+ * @since 0.10.14
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(int length, @NotNull Priority priority, @NotNull PaginatedPane pages) {
+ this(Slot.fromXY(0, 0), 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.10.14
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(int length, @NotNull PaginatedPane pages, @NotNull Plugin plugin) {
+ this(Slot.fromXY(0, 0), 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.10.14
+ * @throws IllegalArgumentException if the length is less than 2
+ */
+ public PagingButtons(int length, @NotNull PaginatedPane pages) {
+ this(Slot.fromXY(0, 0), length, Priority.NORMAL, pages);
+ }
+
+ @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);
+
+ Slot paneSlot = getSlot();
+
+ int xPosition = paneSlot.getX(maxLength);
+ int yPosition = paneSlot.getY(maxLength);
+
+ int totalLength = inventoryComponent.getLength();
+
+ int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY);
+
+ int x = adjustedSlot % totalLength;
+ int y = adjustedSlot / totalLength;
+
+ //this isn't our item
+ if (x < 0 || x >= length || y < 0 || y >= height) {
+ 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;
+ }
+
+ @Override
+ public void display(
+ @NotNull InventoryComponent inventoryComponent,
+ int paneOffsetX,
+ int paneOffsetY,
+ int maxLength,
+ int maxHeight
+ ) {
+ int length = Math.min(getLength(), maxLength);
+
+ int x = super.slot.getX(length) + paneOffsetX;
+ int y = super.slot.getY(length) + paneOffsetY;
+
+ if (this.keepButtonsVisible || this.pages.getPage() > 0) {
+ inventoryComponent.setItem(this.backwardButton, x, y);
+ }
+
+ if (this.keepButtonsVisible || this.pages.getPage() < this.pages.getPages() - 1) {
+ inventoryComponent.setItem(this.forwardButton, x + length - 1, y);
+ }
+ }
+
+ /**
+ * {@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(getSlot(), 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..3eec42066 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
@@ -7,7 +7,10 @@
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.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Element;
@@ -19,10 +22,104 @@
*/
public class PercentageBar extends VariableBar {
+ /**
+ * Creates a new percentage bar
+ *
+ * @param slot the slot of the 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.10.8
+ */
+ public PercentageBar(@NotNull Slot slot, int length, int height, @NotNull Priority priority,
+ @NotNull Plugin plugin) {
+ super(slot, length, height, priority, plugin);
+ }
+
+ /**
+ * Creates a new percentage bar
+ *
+ * @param x the x coordinate of the bar
+ * @param y the y coordinate of the 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.10.8
+ */
+ public PercentageBar(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) {
+ super(x, y, length, height, priority, plugin);
+ }
+
+ /**
+ * Creates a new percentage bar
+ *
+ * @param slot the slot of the 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.10.8
+ */
+ public PercentageBar(@NotNull Slot slot, int length, int height, @NotNull Plugin plugin) {
+ super(slot, length, height, plugin);
+ }
+
+ /**
+ * Creates a new percentage bar
+ *
+ * @param x the x coordinate of the bar
+ * @param y the y coordinate of the 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.10.8
+ */
+ public PercentageBar(int x, int y, int length, int height, @NotNull Plugin plugin) {
+ super(x, y, length, height, plugin);
+ }
+
+ /**
+ * 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.10.8
+ */
+ public PercentageBar(int length, int height, @NotNull Plugin plugin) {
+ super(length, height, plugin);
+ }
+
+ /**
+ * Creates a new percentage bar
+ *
+ * @param slot the slot of the bar
+ * @param length the length of the bar
+ * @param height the height of the bar
+ * @param priority the priority of the bar
+ * @since 0.10.8
+ */
+ public PercentageBar(@NotNull Slot slot, int length, int height, @NotNull Priority priority) {
+ super(slot, length, height, priority);
+ }
+
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 slot the slot of the bar
+ * @param length the length of the bar
+ * @param height the height of the bar
+ * @since 0.10.8
+ */
+ public PercentageBar(@NotNull Slot slot, int length, int height) {
+ super(slot, length, height);
+ }
+
public PercentageBar(int x, int y, int length, int height) {
super(x, y, length, height);
}
@@ -38,10 +135,17 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
int length = Math.min(this.length, maxLength);
int height = Math.min(this.height, maxHeight);
- int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY);
+ Slot paneSlot = getSlot();
+
+ int xPosition = paneSlot.getX(maxLength);
+ int yPosition = paneSlot.getY(maxLength);
+
+ int totalLength = inventoryComponent.getLength();
- int x = adjustedSlot % inventoryComponent.getLength();
- int y = adjustedSlot / inventoryComponent.getLength();
+ int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY);
+
+ int x = adjustedSlot % totalLength;
+ int y = adjustedSlot / totalLength;
if (x < 0 || x >= length || y < 0 || y >= height) {
return false;
@@ -51,8 +155,8 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
event.setCancelled(true);
- int newPaneOffsetX = paneOffsetX + getX();
- int newPaneOffsetY = paneOffsetY + getY();
+ int newPaneOffsetX = paneOffsetX + xPosition;
+ int newPaneOffsetY = paneOffsetY + yPosition;
return this.fillPane.click(
@@ -79,7 +183,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(getSlot(), length, height, getPriority());
applyContents(percentageBar);
@@ -100,23 +204,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,10 +249,26 @@ 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);
}
}
return percentageBar;
}
+
+ /**
+ * Loads a percentage bar from a given element
+ *
+ * @param instance the instance class
+ * @param element the element
+ * @return the percentage bar
+ * @deprecated this method is no longer used internally and has been superseded by
+ * {@link #load(Object, Element, Plugin)}
+ */
+ @NotNull
+ @Contract(pure = true)
+ @Deprecated
+ public static PercentageBar load(@NotNull Object instance, @NotNull Element element) {
+ return load(instance, element, JavaPlugin.getProvidingPlugin(PercentageBar.class));
+ }
}
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..905940f68 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
@@ -7,7 +7,10 @@
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.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Element;
@@ -19,10 +22,103 @@
*/
public class Slider extends VariableBar {
+ /**
+ * Creates a new slider
+ *
+ * @param slot the slot of the 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.10.8
+ */
+ public Slider(@NotNull Slot slot, int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) {
+ super(slot, length, height, priority, plugin);
+ }
+
+ /**
+ * Creates a new slider
+ *
+ * @param x the x coordinate of the slider
+ * @param y the y coordinate of the slier
+ * @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.10.8
+ */
+ public Slider(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) {
+ super(x, y, length, height, priority, plugin);
+ }
+
+ /**
+ * Creates a new slider
+ *
+ * @param slot the slot of the 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.10.8
+ */
+ public Slider(@NotNull Slot slot, int length, int height, @NotNull Plugin plugin) {
+ super(slot, length, height, plugin);
+ }
+
+ /**
+ * Creates a new slider
+ *
+ * @param x the x coordinate of the slider
+ * @param y the y coordinate of the slier
+ * @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.10.8
+ */
+ public Slider(int x, int y, int length, int height, @NotNull Plugin plugin) {
+ super(x, y, length, height, plugin);
+ }
+
+ /**
+ * 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.10.8
+ */
+ public Slider(int length, int height, @NotNull Plugin plugin) {
+ super(length, height, plugin);
+ }
+
+ /**
+ * Creates a new slider
+ *
+ * @param slot the slot of the slider
+ * @param length the length of the slider
+ * @param height the height of the slider
+ * @param priority the priority of the slider
+ * @since 0.10.8
+ */
+ public Slider(@NotNull Slot slot, int length, int height, @NotNull Priority priority) {
+ super(slot, length, height, priority);
+ }
+
public Slider(int x, int y, int length, int height, @NotNull Priority priority) {
super(x, y, length, height, priority);
}
+ /**
+ * Creates a new slider
+ *
+ * @param slot the slot of the slider
+ * @param length the length of the slider
+ * @param height the height of the slider
+ * @since 0.10.8
+ */
+ public Slider(@NotNull Slot slot, int length, int height) {
+ super(slot, length, height);
+ }
+
public Slider(int x, int y, int length, int height) {
super(x, y, length, height);
}
@@ -38,10 +134,17 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
int length = Math.min(this.length, maxLength);
int height = Math.min(this.height, maxHeight);
- int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY);
+ Slot paneSlot = getSlot();
+
+ int xPosition = paneSlot.getX(maxLength);
+ int yPosition = paneSlot.getY(maxLength);
+
+ int totalLength = inventoryComponent.getLength();
- int x = adjustedSlot % inventoryComponent.getLength();
- int y = adjustedSlot / inventoryComponent.getLength();
+ int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY);
+
+ int x = adjustedSlot % totalLength;
+ int y = adjustedSlot / totalLength;
if (x < 0 || x >= length || y < 0 || y >= height) {
return false;
@@ -57,8 +160,8 @@ public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComp
callOnClick(event);
- int newPaneOffsetX = paneOffsetX + getX();
- int newPaneOffsetY = paneOffsetY + getY();
+ int newPaneOffsetX = paneOffsetX + xPosition;
+ int newPaneOffsetY = paneOffsetY + yPosition;
boolean success = this.fillPane.click(
gui, inventoryComponent, event, slot, newPaneOffsetX, newPaneOffsetY, length, height
@@ -88,7 +191,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(getSlot(), length, height, getPriority());
applyContents(slider);
@@ -109,23 +212,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,10 +257,26 @@ 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);
}
}
return slider;
}
+
+ /**
+ * Loads a percentage bar from a given element
+ *
+ * @param instance the instance class
+ * @param element the element
+ * @return the percentage bar
+ * @deprecated this method is no longer used internally and has been superseded by
+ * {@link #load(Object, Element, Plugin)}
+ */
+ @NotNull
+ @Contract(pure = true)
+ @Deprecated
+ public static Slider load(@NotNull Object instance, @NotNull Element element) {
+ return load(instance, element, JavaPlugin.getProvidingPlugin(Slider.class));
+ }
}
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..f112147b6 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,515 @@
-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.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 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 slot the slot
+ * @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
+ * @see #ToggleButton(int, int, int, int, Priority, boolean)
+ * @since 0.10.8
+ */
+ public ToggleButton(@NotNull Slot slot, int length, int height, @NotNull Priority priority, boolean enabled,
+ @NotNull Plugin plugin) {
+ super(slot, 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 x the x coordinate
+ * @param y the y coordinate
+ * @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
+ * @see #ToggleButton(int, int, int, int, Priority, boolean)
+ * @since 0.10.8
+ */
+ public ToggleButton(int x, int y, int length, int height, @NotNull Priority priority, boolean enabled,
+ @NotNull Plugin plugin) {
+ this(Slot.fromXY(x, y), length, height, priority, enabled, plugin);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param slot the slot
+ * @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
+ * @see #ToggleButton(Slot, int, int, Priority)
+ * @since 0.10.8
+ */
+ public ToggleButton(@NotNull Slot slot, int length, int height, @NotNull Priority priority,
+ @NotNull Plugin plugin) {
+ this(slot, length, height, priority, false, plugin);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @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
+ * @see #ToggleButton(int, int, int, int, Priority)
+ * @since 0.10.8
+ */
+ public ToggleButton(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) {
+ this(x, y, length, height, priority, false, plugin);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param slot the slot
+ * @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
+ * @see #ToggleButton(Slot, int, int, boolean)
+ * @since 0.10.8
+ */
+ public ToggleButton(@NotNull Slot slot, int length, int height, boolean enabled, @NotNull Plugin plugin) {
+ this(slot, length, height, Priority.NORMAL, enabled, plugin);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @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
+ * @see #ToggleButton(int, int, int, int, boolean)
+ * @since 0.10.8
+ */
+ public ToggleButton(int x, int y, int length, int height, boolean enabled, @NotNull Plugin plugin) {
+ this(x, y, length, height, Priority.NORMAL, enabled, plugin);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param slot the slot
+ * @param length the length
+ * @param height the height
+ * @param plugin the plugin that will be the owner of this button's items
+ * @see #ToggleButton(Slot, int, int)
+ * @since 0.10.8
+ */
+ public ToggleButton(@NotNull Slot slot, int length, int height, @NotNull Plugin plugin) {
+ this(slot, length, height, false, plugin);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param length the length
+ * @param height the height
+ * @param plugin the plugin that will be the owner of this button's items
+ * @see #ToggleButton(int, int, int, int)
+ * @since 0.10.8
+ */
+ public ToggleButton(int x, int y, int length, int height, @NotNull Plugin plugin) {
+ this(x, y, length, height, 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
+ * @see #ToggleButton(int, int, boolean)
+ * @since 0.10.8
+ */
+ public ToggleButton(int length, int height, boolean enabled, @NotNull Plugin plugin) {
+ this(0, 0, length, height, enabled);
+ }
+
+ /**
+ * 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
+ * @see #ToggleButton(int, int)
+ * @since 0.10.8
+ */
+ public ToggleButton(int length, int height, @NotNull Plugin plugin) {
+ this(length, height, false);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param slot the slot
+ * @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.10.8
+ */
+ public ToggleButton(@NotNull Slot slot, int length, int height, @NotNull Priority priority, boolean enabled) {
+ this(slot, length, height, priority, enabled, JavaPlugin.getProvidingPlugin(ToggleButton.class));
+ }
+
+ public ToggleButton(int x, int y, int length, int height, @NotNull Priority priority, boolean enabled) {
+ this(x, y, length, height, priority, enabled, JavaPlugin.getProvidingPlugin(ToggleButton.class));
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param slot the slot
+ * @param length the length
+ * @param height the height
+ * @param priority the priority
+ * @since 0.10.8
+ */
+ public ToggleButton(@NotNull Slot slot, int length, int height, @NotNull Priority priority) {
+ this(slot, length, height, priority, false);
+ }
+
+ public ToggleButton(int x, int y, int length, int height, @NotNull Priority priority) {
+ this(x, y, length, height, priority, false);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param slot the slot
+ * @param length the length
+ * @param height the height
+ * @param enabled whether the button should start in its enabled or disabled state
+ * @since 0.10.8
+ */
+ public ToggleButton(@NotNull Slot slot, int length, int height, boolean enabled) {
+ this(slot, length, height, Priority.NORMAL, enabled);
+ }
+
+ public ToggleButton(int x, int y, int length, int height, boolean enabled) {
+ this(x, y, length, height, Priority.NORMAL, enabled);
+ }
+
+ /**
+ * Creates a new toggle button
+ *
+ * @param slot the slot
+ * @param length the length
+ * @param height the height
+ * @since 0.10.8
+ */
+ public ToggleButton(@NotNull Slot slot, int length, int height) {
+ this(slot, length, height, false);
+ }
+
+ public ToggleButton(int x, int y, int length, int height) {
+ this(x, y, length, height, false);
+ }
+
+ public ToggleButton(int length, int height, boolean enabled) {
+ this(0, 0, length, height, enabled);
+ }
+
+ public ToggleButton(int length, int height) {
+ this(length, height, false);
+ }
+
+ @Override
+ public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength,
+ int maxHeight) {
+ int newMaxLength = Math.min(maxLength, length);
+ int newMaxHeight = Math.min(maxHeight, height);
+
+ int newPaneOffsetX = this.slot.getX(newMaxLength) + paneOffsetX;
+ int newPaneOffsetY = this.slot.getY(newMaxHeight) + paneOffsetY;
+
+ if (enabled) {
+ this.enabledPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, newMaxLength, newMaxHeight);
+ } else {
+ this.disabledPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, 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);
+
+ Slot paneSlot = getSlot();
+
+ int xPosition = paneSlot.getX(maxLength);
+ int yPosition = paneSlot.getY(maxLength);
+
+ int totalLength = inventoryComponent.getLength();
+
+ int adjustedSlot = slot - (xPosition + paneOffsetX) - totalLength * (yPosition + paneOffsetY);
+
+ int x = adjustedSlot % totalLength;
+ int y = adjustedSlot / totalLength;
+
+ //this isn't our item
+ if (x < 0 || x >= length || y < 0 || y >= height) {
+ return false;
+ }
+
+ if (this.allowToggle) {
+ toggle();
+ }
+
+ callOnClick(event);
+
+ int newX = paneOffsetX + xPosition;
+ int newY = paneOffsetY + yPosition;
+
+ /*
+ 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) {
+ disabledPane.click(gui, inventoryComponent, event, slot, newX, newY, length, height);
+ } else {
+ enabledPane.click(gui, inventoryComponent, event, slot, newX, newY, length, height);
+ }
+
+ event.setCancelled(true);
+
+ gui.update();
+
+ return true;
+ }
+
+ @NotNull
+ @Contract(pure = true)
+ @Override
+ public ToggleButton copy() {
+ ToggleButton toggleButton = new ToggleButton(getSlot(), length, height, getPriority(), enabled);
+
+ 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;
+ }
+
+ /**
+ * 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
+ * @deprecated this method is no longer used internally and has been superseded by
+ * {@link #load(Object, Element, Plugin)}
+ */
+ @NotNull
+ @Contract(pure = true)
+ @Deprecated
+ public static ToggleButton load(@NotNull Object instance, @NotNull Element element) {
+ return load(instance, element, JavaPlugin.getProvidingPlugin(ToggleButton.class));
+ }
+}
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..e3a2d0507 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
@@ -6,8 +6,11 @@
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.Slot;
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,8 +46,33 @@ public abstract class VariableBar extends Pane implements Orientable, Flippable
*/
protected boolean flipHorizontally, flipVertically;
- protected VariableBar(int length, int height) {
- super(length, 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(0, 0, length, height, plugin);
+ }
+
+ /**
+ * Creates a new variable bar
+ *
+ * @param slot the slot of the 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
+ * @see #VariableBar(Slot, int, int, Priority)
+ * @since 0.10.8
+ */
+ protected VariableBar(@NotNull Slot slot, int length, int height, @NotNull Priority priority,
+ @NotNull Plugin plugin) {
+ super(slot, length, height);
this.value = 0F;
this.orientation = Orientation.HORIZONTAL;
@@ -53,23 +81,94 @@ protected VariableBar(int length, int height) {
this.backgroundPane = new OutlinePane(0, 0, 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);
+
+ setPriority(priority);
}
- protected VariableBar(int x, int y, int length, int height, @NotNull Priority priority) {
- this(length, height);
+ /**
+ * Creates a new variable bar
+ *
+ * @param x the x coordinate of the bar
+ * @param y the y coordinate of the 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
+ * @see #VariableBar(int, int)
+ * @since 0.10.8
+ */
+ protected VariableBar(int x, int y, int length, int height, @NotNull Priority priority, @NotNull Plugin plugin) {
+ this(Slot.fromXY(x, y), length, height, priority, plugin);
+ }
- setX(x);
- setY(y);
+ /**
+ * Creates a new variable bar
+ *
+ * @param slot the slot of the 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(Slot, int, int)
+ * @since 0.10.8
+ */
+ protected VariableBar(@NotNull Slot slot, int length, int height, @NotNull Plugin plugin) {
+ this(slot, length, height, Priority.NORMAL, plugin);
+ }
- setPriority(priority);
+ /**
+ * Creates a new variable bar
+ *
+ * @param x the x coordinate of the bar
+ * @param y the y coordinate of the 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 x, int y, int length, int height, @NotNull Plugin plugin) {
+ this(x, y, length, height, Priority.NORMAL, plugin);
+ }
+
+ protected VariableBar(int length, int height) {
+ this(0, 0, length, height);
+ }
+
+ /**
+ * Creates a new variable bar
+ *
+ * @param slot the slot of the bar
+ * @param length the length of the bar
+ * @param height the height of the bar
+ * @param priority the priority of the bar
+ * @since 0.10.8
+ */
+ protected VariableBar(@NotNull Slot slot, int length, int height, @NotNull Priority priority) {
+ this(slot, length, height, priority, JavaPlugin.getProvidingPlugin(VariableBar.class));
+ }
+
+ protected VariableBar(int x, int y, int length, int height, @NotNull Priority priority) {
+ this(x, y, length, height, priority, JavaPlugin.getProvidingPlugin(VariableBar.class));
+ }
+
+ /**
+ * Creates a new variable bar
+ *
+ * @param slot the slot of the bar
+ * @param length the length of the bar
+ * @param height the height of the bar
+ * @since 0.10.8
+ */
+ protected VariableBar(@NotNull Slot slot, int length, int height) {
+ this(slot, length, height, Priority.NORMAL);
}
protected VariableBar(int x, int y, int length, int height) {
@@ -184,6 +283,7 @@ public void setHeight(int height) {
protected void applyContents(@NotNull VariableBar copy) {
copy.x = x;
copy.y = y;
+ copy.slot = slot;
copy.length = length;
copy.height = height;
copy.setPriority(getPriority());
@@ -236,13 +336,20 @@ public void setOrientation(@NotNull Orientation orientation) {
@Override
public void display(@NotNull InventoryComponent inventoryComponent, int paneOffsetX, int paneOffsetY, int maxLength,
int maxHeight) {
- int newPaneOffsetX = paneOffsetX + getX();
- int newPaneOffsetY = paneOffsetY + getY();
+ Slot slot = getSlot();
+
+ int newPaneOffsetX = paneOffsetX + slot.getX(maxLength);
+ int newPaneOffsetY = paneOffsetY + slot.getY(maxLength);
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);
+ if (this.backgroundPane.isVisible()) {
+ this.backgroundPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, newMaxLength, newMaxHeight);
+ }
+
+ if (this.fillPane.isVisible()) {
+ this.fillPane.display(inventoryComponent, newPaneOffsetX, newPaneOffsetY, newMaxLength, newMaxHeight);
+ }
}
/**
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/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..1b4045117 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,198 @@
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_16_R3;
+ 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;
+
+ /**
+ * 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
+ );
+
+ /**
+ * 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
+ );
+
+ /**
+ * 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 +220,50 @@ 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];
+
+ 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..8a99e4e51 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,311 @@ 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);
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);
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);
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);
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);
+
+ 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);
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);
+
+ 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);
}
}
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..29f0a4411 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
@@ -24,6 +24,7 @@ void testCopy() {
original.flipHorizontally(true);
original.flipVertically(true);
original.applyMask(new Mask("0"));
+ original.align(OutlinePane.Alignment.CENTER);
OutlinePane copy = original.copy();
@@ -42,6 +43,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..6df185eea 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,15 +1,140 @@
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 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(0, 0, 1, 1);
+
+ StaticPane staticPane = new StaticPane(0, 0, 1, 1);
+
+ assertDoesNotThrow(() -> {
+ paginatedPane.addPage(staticPane);
+
+ Collection panes = paginatedPane.getPanes(0);
+
+ assertEquals(1, panes.size());
+ assertSame(staticPane, panes.iterator().next());
+ });
+ }
+
+ @Test
+ void testAddPageNotEmpty() {
+ PaginatedPane paginatedPane = new PaginatedPane(0, 0, 1, 1);
+
+ StaticPane staticPane1 = new StaticPane(0, 0, 1, 1);
+ StaticPane staticPane2 = new StaticPane(0, 0, 1, 1);
+
+ paginatedPane.addPane(0, staticPane1);
+
+ assertDoesNotThrow(() -> {
+ paginatedPane.addPage(staticPane2);
+
+ Collection panes = paginatedPane.getPanes(1);
+
+ assertEquals(1, panes.size());
+ assertSame(staticPane2, panes.iterator().next());
+ });
+ }
+
+ @Test
+ void testAddPaneNegative() {
+ PaginatedPane paginatedPane = new PaginatedPane(0, 0, 1, 1);
+
+ StaticPane staticPane = new StaticPane(0, 0, 1, 1);
+
+ assertThrows(IllegalArgumentException.class, () -> paginatedPane.addPane(-1, staticPane));
+ }
+
+ @Test
+ void testAddPaneExisting() {
+ PaginatedPane paginatedPane = new PaginatedPane(0, 0, 1, 1);
+
+ StaticPane staticPane1 = new StaticPane(0, 0, 1, 1);
+ StaticPane staticPane2 = new StaticPane(0, 0, 1, 1);
+
+ Set super Pane> elements = new HashSet<>();
+
+ elements.add(staticPane1);
+ elements.add(staticPane2);
+
+ paginatedPane.addPane(0, staticPane1);
+
+ assertDoesNotThrow(() -> {
+ paginatedPane.addPane(0, staticPane2);
+
+ Collection panes = paginatedPane.getPanes(0);
+
+ assertEquals(elements.size(), panes.size());
+ assertTrue(elements.containsAll(panes));
+ });
+ }
+
+ @Test
+ void testAddPaneAfter() {
+ PaginatedPane paginatedPane = new PaginatedPane(0, 0, 1, 1);
+
+ StaticPane staticPane1 = new StaticPane(0, 0, 1, 1);
+ StaticPane staticPane2 = new StaticPane(0, 0, 1, 1);
+
+ paginatedPane.addPane(0, staticPane1);
+
+ assertDoesNotThrow(() -> {
+ paginatedPane.addPane(1, 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(0, 0, 1, 1);
+
+ StaticPane staticPane1 = new StaticPane(0, 0, 1, 1);
+ StaticPane staticPane2 = new StaticPane(0, 0, 1, 1);
+
+ paginatedPane.addPane(0, staticPane1);
+
+ assertThrows(IllegalArgumentException.class, () -> paginatedPane.addPane(2, staticPane2));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {-1, 0})
+ void testSetPageOutside(int index) {
+ PaginatedPane paginatedPane = new PaginatedPane(0, 0, 1, 1);
+
+ assertThrows(ArrayIndexOutOfBoundsException.class, () -> paginatedPane.setPage(index));
+ }
+
+ @Test
+ void testSetPage() {
+ PaginatedPane paginatedPane = new PaginatedPane(0, 0, 1, 1);
+
+ StaticPane staticPane1 = new StaticPane(0, 0, 1, 1);
+
+ paginatedPane.addPage(staticPane1);
+
+ assertDoesNotThrow(() -> paginatedPane.setPage(0));
+ }
+
@Test
void testCopy() {
PaginatedPane original = new PaginatedPane(5, 5, 4, 1, Pane.Priority.NORMAL);
@@ -37,4 +162,28 @@ void testCopy() {
assertEquals(original.getPages(), copy.getPages());
assertEquals(original.getUUID(), copy.getUUID());
}
+
+ @Test
+ void testDeletePageExists() {
+ PaginatedPane pane = new PaginatedPane(0, 0, 1, 1);
+
+ StaticPane staticPane = new StaticPane(0, 0, 1, 1);
+
+ pane.addPane(0, new StaticPane(0, 0, 1, 1));
+ pane.addPane(1, 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(0, 0, 1, 1);
+
+ assertDoesNotThrow(() -> pane.deletePage(index));
+ }
}
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..1808ba909 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
@@ -15,7 +15,6 @@ public class PaginatedPaneTest {
void testGetPanesNonExistentPage() {
PaginatedPane pane = new PaginatedPane(1, 1);
- //noinspection ResultOfMethodCallIgnored
assertThrows(IllegalArgumentException.class, () -> pane.getPanes(0));
}
diff --git a/README.md b/README.md
index a6d6353e1..592956aca 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, and 1.20-1.21*
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.11.6
```
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.11.6'
// ...
}
```
@@ -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,135 @@ 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.8, 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
+```
+
+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..4c7eac3b7
--- /dev/null
+++ b/adventure-support/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ IF-parent
+ com.github.stefvanschie.inventoryframework
+ 0.12.0-SNAPSHOT
+
+ 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_16_R1/pom.xml b/inventory-view/iv-abstract-class/pom.xml
similarity index 55%
rename from nms/1_16_R1/pom.xml
rename to inventory-view/iv-abstract-class/pom.xml
index 47e02eab6..9638b29e7 100644
--- a/nms/1_16_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-SNAPSHOT
../../pom.xml
- 4.0.0
- 1_16_R1
+ iv-abstract-class
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.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..3746dbad5 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-SNAPSHOT
../../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_15_R1/pom.xml b/inventory-view/iv-interface/pom.xml
similarity index 56%
rename from nms/1_15_R1/pom.xml
rename to inventory-view/iv-interface/pom.xml
index 8cbdc5417..19ea6d9f4 100644
--- a/nms/1_15_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-SNAPSHOT
../../pom.xml
- 4.0.0
- 1_15_R1
+ iv-interface
true
+
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
+
+
-
- org.spigotmc
- spigot
- 1.15.2-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/pom.xml b/nms/1_14_R1/pom.xml
deleted file mode 100644
index 6dbcde799..000000000
--- a/nms/1_14_R1/pom.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
- IF-parent
- com.github.stefvanschie.inventoryframework
- 0.9.8
- ../../pom.xml
-
- 4.0.0
-
- 1_14_R1
-
-
- true
-
-
-
-
- org.spigotmc
- spigot
- 1.14.4-R0.1-SNAPSHOT
- provided
-
-
- com.github.stefvanschie.inventoryframework
- abstraction
- ${project.version}
- compile
-
-
-
\ No newline at end of file
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 81%
rename from nms/1_16_R3/pom.xml
rename to nms/1_16_5/pom.xml
index 5ddb316fd..a70bbb7cb 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-SNAPSHOT
../../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..6eab7f160
--- /dev/null
+++ b/nms/1_17_1/pom.xml
@@ -0,0 +1,59 @@
+
+
+
+ IF-parent
+ com.github.stefvanschie.inventoryframework
+ 0.12.0-SNAPSHOT
+ ../../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..48d5cd5be
--- /dev/null
+++ b/nms/1_18_2/pom.xml
@@ -0,0 +1,59 @@
+
+
+
+ IF-parent
+ com.github.stefvanschie.inventoryframework
+ 0.12.0-SNAPSHOT
+ ../../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..9719f1edc
--- /dev/null
+++ b/nms/1_19_4/pom.xml
@@ -0,0 +1,58 @@
+
+
+
+ IF-parent
+ com.github.stefvanschie.inventoryframework
+ 0.12.0-SNAPSHOT
+ ../../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..40dffc488
--- /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-SNAPSHOT
+ ../../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..0071336a1
--- /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-SNAPSHOT
+ ../../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..a3860e7a0
--- /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-SNAPSHOT
+ ../../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..967d44d19
--- /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-SNAPSHOT
+ ../../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.25.0
+
+
+ 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.25.0
+
+
+ net.kyori
+ adventure-text-serializer-legacy
+ 4.26.1
+
+
+ net.kyori
+ adventure-text-serializer-plain
+ 4.25.0
+
+
+
+
+
+
+
+ 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..d107d2f37
--- /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-SNAPSHOT
+ ../../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
+
+
+ package
+
+ remap
+
+ remap-obf
+
+ org.spigotmc:minecraft-server:1.20.5-R0.1-SNAPSHOT:txt:maps-mojang
+ true
+ org.spigotmc:spigot:1.20.5-R0.1-SNAPSHOT:jar:remapped-mojang
+ true
+ remapped-obf
+
+
+
+ package
+
+ remap
+
+ remap-spigot
+
+ ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar
+ org.spigotmc:minecraft-server:1.20.5-R0.1-SNAPSHOT:csrg:maps-spigot
+ org.spigotmc:spigot:1.20.5-R0.1-SNAPSHOT:jar:remapped-obf
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/AnvilInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/AnvilInventoryImpl.java
new file mode 100644
index 000000000..55df10daa
--- /dev/null
+++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/AnvilInventoryImpl.java
@@ -0,0 +1,228 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_5.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_R4.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_20_R4.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.5
+ *
+ * @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 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);
+ }
+
+ @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(Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, ClickType clickType, 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_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/BeaconInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/BeaconInventoryImpl.java
new file mode 100644
index 000000000..88a260767
--- /dev/null
+++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/BeaconInventoryImpl.java
@@ -0,0 +1,161 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_5;
+
+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_R4.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_20_R4.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.5
+ *
+ * @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_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/CartographyTableInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..55786c33e
--- /dev/null
+++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/CartographyTableInventoryImpl.java
@@ -0,0 +1,196 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_5.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_R4.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_20_R4.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.5
+ *
+ * @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_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/EnchantingTableInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..9502dc374
--- /dev/null
+++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/EnchantingTableInventoryImpl.java
@@ -0,0 +1,180 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_5.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_R4.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_20_R4.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.5
+ *
+ * @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_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/GrindstoneInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..0484b7080
--- /dev/null
+++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_5.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_R4.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_20_R4.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.5
+ *
+ * @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_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/MerchantInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/MerchantInventoryImpl.java
new file mode 100644
index 000000000..3f0d98e5a
--- /dev/null
+++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/MerchantInventoryImpl.java
@@ -0,0 +1,321 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_5.util.TextHolderUtil;
+import net.minecraft.core.component.DataComponentPredicate;
+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.ItemCost;
+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_R4.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView;
+import org.bukkit.craftbukkit.v1_20_R4.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;
+import java.util.Optional;
+
+/**
+ * Internal merchant inventory for 1.20.5
+ *
+ * @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);
+ }
+
+ ItemCost itemCostA = convertItemStackToItemCost(nmsItemA);
+ ItemCost itemCostB = convertItemStackToItemCost(nmsItemB);
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ itemCostA, Optional.of(itemCostB), 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);
+ }
+
+ /**
+ * Converts an NMS item stack to an item cost.
+ *
+ * @param itemStack the item stack to convert
+ * @return the item cost
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(value = "_ -> new", pure = true)
+ private ItemCost convertItemStackToItemCost(@NotNull net.minecraft.world.item.ItemStack itemStack) {
+ DataComponentPredicate predicate = DataComponentPredicate.allOf(itemStack.getComponents());
+
+ return new ItemCost(itemStack.getItemHolder(), itemStack.getCount(), predicate, itemStack);
+ }
+
+ /**
+ * 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_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/SmithingTableInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..7019d6f9b
--- /dev/null
+++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/SmithingTableInventoryImpl.java
@@ -0,0 +1,217 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_5.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_R4.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_20_R4.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.5. 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_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/StonecutterInventoryImpl.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..8326ad8a4
--- /dev/null
+++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/StonecutterInventoryImpl.java
@@ -0,0 +1,200 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_5;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_5.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_R4.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_20_R4.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.5
+ *
+ * @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_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/CustomInventoryUtil.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..b735c1655
--- /dev/null
+++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_5.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R4.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_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/TextHolderUtil.java b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/TextHolderUtil.java
new file mode 100644
index 000000000..9f8bce785
--- /dev/null
+++ b/nms/1_20_5/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_5/util/TextHolderUtil.java
@@ -0,0 +1,67 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_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.core.HolderLookup;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/**
+ * 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(), HolderLookup.Provider.create(Stream.empty())));
+ }
+}
diff --git a/nms/1_20_6/pom.xml b/nms/1_20_6/pom.xml
new file mode 100644
index 000000000..192a9e33d
--- /dev/null
+++ b/nms/1_20_6/pom.xml
@@ -0,0 +1,73 @@
+
+
+ 4.0.0
+
+ com.github.stefvanschie.inventoryframework
+ IF-parent
+ 0.12.0-SNAPSHOT
+ ../../pom.xml
+
+
+ 1_20_6
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ org.spigotmc
+ spigot
+ 1.20.6-R0.1-SNAPSHOT
+ remapped-mojang
+ provided
+
+
+
+
+
+
+ net.md-5
+ specialsource-maven-plugin
+ 2.0.4
+
+
+ package
+
+ remap
+
+ remap-obf
+
+ org.spigotmc:minecraft-server:1.20.6-R0.1-SNAPSHOT:txt:maps-mojang
+ true
+ org.spigotmc:spigot:1.20.6-R0.1-SNAPSHOT:jar:remapped-mojang
+ true
+ remapped-obf
+
+
+
+ package
+
+ remap
+
+ remap-spigot
+
+ ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar
+ org.spigotmc:minecraft-server:1.20.6-R0.1-SNAPSHOT:csrg:maps-spigot
+ org.spigotmc:spigot:1.20.6-R0.1-SNAPSHOT:jar:remapped-obf
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/AnvilInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/AnvilInventoryImpl.java
new file mode 100644
index 000000000..b0bee3a19
--- /dev/null
+++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/AnvilInventoryImpl.java
@@ -0,0 +1,228 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_6;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_6.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_R4.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_20_R4.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.6
+ *
+ * @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);
+ }
+
+ @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(Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, ClickType clickType, 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_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/BeaconInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/BeaconInventoryImpl.java
new file mode 100644
index 000000000..e64a6f1a9
--- /dev/null
+++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/BeaconInventoryImpl.java
@@ -0,0 +1,161 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_6;
+
+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_R4.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_20_R4.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.6
+ *
+ * @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_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/CartographyTableInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..4ce31ad87
--- /dev/null
+++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/CartographyTableInventoryImpl.java
@@ -0,0 +1,196 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_6;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_6.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_R4.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_20_R4.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.6
+ *
+ * @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_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/EnchantingTableInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..7ef6ad8c8
--- /dev/null
+++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/EnchantingTableInventoryImpl.java
@@ -0,0 +1,180 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_6;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_6.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_R4.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_20_R4.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.6
+ *
+ * @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_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/GrindstoneInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..663de70de
--- /dev/null
+++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_6;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_6.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_R4.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_20_R4.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.6
+ *
+ * @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_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/MerchantInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/MerchantInventoryImpl.java
new file mode 100644
index 000000000..72649306d
--- /dev/null
+++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/MerchantInventoryImpl.java
@@ -0,0 +1,321 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_6;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.TextHolderUtil;
+import net.minecraft.core.component.DataComponentPredicate;
+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.ItemCost;
+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_R4.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView;
+import org.bukkit.craftbukkit.v1_20_R4.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;
+import java.util.Optional;
+
+/**
+ * Internal merchant inventory for 1.20.6
+ *
+ * @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);
+ }
+
+ ItemCost itemCostA = convertItemStackToItemCost(nmsItemA);
+ ItemCost itemCostB = convertItemStackToItemCost(nmsItemB);
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ itemCostA, Optional.of(itemCostB), 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);
+ }
+
+ /**
+ * Converts an NMS item stack to an item cost.
+ *
+ * @param itemStack the item stack to convert
+ * @return the item cost
+ * @since 0.10.14
+ */
+ @NotNull
+ @Contract(value = "_ -> new", pure = true)
+ private ItemCost convertItemStackToItemCost(@NotNull net.minecraft.world.item.ItemStack itemStack) {
+ DataComponentPredicate predicate = DataComponentPredicate.allOf(itemStack.getComponents());
+
+ return new ItemCost(itemStack.getItemHolder(), itemStack.getCount(), predicate, itemStack);
+ }
+
+ /**
+ * 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_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/SmithingTableInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..524ff44a5
--- /dev/null
+++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/SmithingTableInventoryImpl.java
@@ -0,0 +1,217 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_6;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_6.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_R4.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_20_R4.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.6. 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_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/StonecutterInventoryImpl.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..f50008ba5
--- /dev/null
+++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/StonecutterInventoryImpl.java
@@ -0,0 +1,200 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_6;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_20_6.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_R4.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_20_R4.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.6
+ *
+ * @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_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/CustomInventoryUtil.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..a0f724f18
--- /dev/null
+++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_6.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_20_R4.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_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/TextHolderUtil.java b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/TextHolderUtil.java
new file mode 100644
index 000000000..8e945b8bb
--- /dev/null
+++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/util/TextHolderUtil.java
@@ -0,0 +1,67 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_20_6.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.core.HolderLookup;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/**
+ * 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(), HolderLookup.Provider.create(Stream.empty())));
+ }
+}
diff --git a/nms/1_21_0/pom.xml b/nms/1_21_0/pom.xml
new file mode 100644
index 000000000..4ae97038c
--- /dev/null
+++ b/nms/1_21_0/pom.xml
@@ -0,0 +1,72 @@
+
+
+ 4.0.0
+
+ com.github.stefvanschie.inventoryframework
+ IF-parent
+ 0.12.0-SNAPSHOT
+ ../../pom.xml
+
+
+ 1_21_0
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ org.spigotmc
+ spigot
+ 1.21-R0.1-SNAPSHOT
+ remapped-mojang
+ provided
+
+
+
+
+
+
+ net.md-5
+ specialsource-maven-plugin
+ 2.0.4
+
+
+ package
+
+ remap
+
+ remap-obf
+
+ org.spigotmc:minecraft-server:1.21-R0.1-SNAPSHOT:txt:maps-mojang
+ true
+ org.spigotmc:spigot:1.21-R0.1-SNAPSHOT:jar:remapped-mojang
+ true
+ remapped-obf
+
+
+
+ package
+
+ remap
+
+ remap-spigot
+
+ ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar
+ org.spigotmc:minecraft-server:1.21-R0.1-SNAPSHOT:csrg:maps-spigot
+ org.spigotmc:spigot:1.21-R0.1-SNAPSHOT:jar:remapped-obf
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/AnvilInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/AnvilInventoryImpl.java
new file mode 100644
index 000000000..e69e59418
--- /dev/null
+++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/AnvilInventoryImpl.java
@@ -0,0 +1,228 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftAnvilView;
+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.21.0
+ *
+ * @since 0.10.18
+ */
+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) {
+ @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.18
+ */
+ 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 CraftAnvilView 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);
+ }
+
+ @Override
+ public CraftAnvilView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(
+ this.access.getLocation(),
+ this.inputSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftAnvilView(this.player.getBukkitEntity(), inventory, this);
+ this.bukkitEntity.updateFromLegacy(inventory);
+
+ 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(Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, ClickType clickType, 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_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/BeaconInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/BeaconInventoryImpl.java
new file mode 100644
index 000000000..1f9c5b897
--- /dev/null
+++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/BeaconInventoryImpl.java
@@ -0,0 +1,161 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftBeaconView;
+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.21.0
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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 CraftBeaconView 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 CraftBeaconView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryBeacon inventory = new CraftInventoryBeacon(this.inputSlot);
+
+ this.bukkitEntity = new CraftBeaconView(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_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/CartographyTableInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..a47c86266
--- /dev/null
+++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/CartographyTableInventoryImpl.java
@@ -0,0 +1,196 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_21_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.21.0
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/EnchantingTableInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..8aa8d9a50
--- /dev/null
+++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/EnchantingTableInventoryImpl.java
@@ -0,0 +1,180 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftEnchantmentView;
+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.21.0
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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 CraftEnchantmentView 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 CraftEnchantmentView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.inputSlots);
+
+ this.bukkitEntity = new CraftEnchantmentView(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_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/GrindstoneInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..d9bdf6165
--- /dev/null
+++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_21_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.21.0
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/MerchantInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/MerchantInventoryImpl.java
new file mode 100644
index 000000000..d46b41ad5
--- /dev/null
+++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/MerchantInventoryImpl.java
@@ -0,0 +1,321 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_0.util.TextHolderUtil;
+import net.minecraft.core.component.DataComponentPredicate;
+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.ItemCost;
+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_21_R1.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftMerchantView;
+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;
+import java.util.Optional;
+
+/**
+ * Internal merchant inventory for 1.21.0
+ *
+ * @since 0.10.18
+ */
+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);
+ }
+
+ ItemCost itemCostA = convertItemStackToItemCost(nmsItemA);
+ ItemCost itemCostB = convertItemStackToItemCost(nmsItemB);
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ itemCostA, Optional.of(itemCostB), 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);
+ }
+
+ /**
+ * Converts an NMS item stack to an item cost.
+ *
+ * @param itemStack the item stack to convert
+ * @return the item cost
+ * @since 0.10.18
+ */
+ @NotNull
+ @Contract(value = "_ -> new", pure = true)
+ private ItemCost convertItemStackToItemCost(@NotNull net.minecraft.world.item.ItemStack itemStack) {
+ DataComponentPredicate predicate = DataComponentPredicate.allOf(itemStack.getComponents());
+
+ return new ItemCost(itemStack.getItemHolder(), itemStack.getCount(), predicate, itemStack);
+ }
+
+ /**
+ * 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.18
+ */
+ @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.18
+ */
+ @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 CraftMerchantView 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 CraftMerchantView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryMerchant inventory = new CraftInventoryMerchant(this.merchant, this.container);
+
+ this.bukkitEntity = new CraftMerchantView(this.humanEntity, inventory, this, this.merchant);
+
+ 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_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/SmithingTableInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..973a338ca
--- /dev/null
+++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/SmithingTableInventoryImpl.java
@@ -0,0 +1,217 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_21_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.21.0. This is only available for Minecraft 1.20 and higher.
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/StonecutterInventoryImpl.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..95ffce2bc
--- /dev/null
+++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/StonecutterInventoryImpl.java
@@ -0,0 +1,200 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_0;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftStonecutterView;
+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.21.0
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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 CraftStonecutterView 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 CraftStonecutterView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryStonecutter inventory = new CraftInventoryStonecutter(this.inputSlot, this.resultSlot);
+
+ this.bukkitEntity = new CraftStonecutterView(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_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/CustomInventoryUtil.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..364fd9bca
--- /dev/null
+++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_0.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_21_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.18
+ */
+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.18
+ */
+ @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_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/TextHolderUtil.java b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/TextHolderUtil.java
new file mode 100644
index 000000000..b0757a95b
--- /dev/null
+++ b/nms/1_21_0/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_0/util/TextHolderUtil.java
@@ -0,0 +1,67 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_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.core.HolderLookup;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/**
+ * A utility class for adding {@link TextHolder} support.
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ @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.18
+ */
+ @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.18
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull ComponentHolder holder) {
+ return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson(), HolderLookup.Provider.create(Stream.empty())));
+ }
+}
diff --git a/nms/1_21_1/pom.xml b/nms/1_21_1/pom.xml
new file mode 100644
index 000000000..8c360dde4
--- /dev/null
+++ b/nms/1_21_1/pom.xml
@@ -0,0 +1,72 @@
+
+
+ 4.0.0
+
+ com.github.stefvanschie.inventoryframework
+ IF-parent
+ 0.12.0-SNAPSHOT
+ ../../pom.xml
+
+
+ 1_21_1
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ org.spigotmc
+ spigot
+ 1.21.1-R0.1-SNAPSHOT
+ remapped-mojang
+ provided
+
+
+
+
+
+
+ net.md-5
+ specialsource-maven-plugin
+ 2.0.4
+
+
+ package
+
+ remap
+
+ remap-obf
+
+ org.spigotmc:minecraft-server:1.21.1-R0.1-SNAPSHOT:txt:maps-mojang
+ true
+ org.spigotmc:spigot:1.21.1-R0.1-SNAPSHOT:jar:remapped-mojang
+ true
+ remapped-obf
+
+
+
+ package
+
+ remap
+
+ remap-spigot
+
+ ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar
+ org.spigotmc:minecraft-server:1.21.1-R0.1-SNAPSHOT:csrg:maps-spigot
+ org.spigotmc:spigot:1.21.1-R0.1-SNAPSHOT:jar:remapped-obf
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/AnvilInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/AnvilInventoryImpl.java
new file mode 100644
index 000000000..9dee4d40c
--- /dev/null
+++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/AnvilInventoryImpl.java
@@ -0,0 +1,228 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftAnvilView;
+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.21.1
+ *
+ * @since 0.10.18
+ */
+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) {
+ @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.18
+ */
+ 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 CraftAnvilView 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);
+ }
+
+ @Override
+ public CraftAnvilView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(
+ this.access.getLocation(),
+ this.inputSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftAnvilView(this.player.getBukkitEntity(), inventory, this);
+ this.bukkitEntity.updateFromLegacy(inventory);
+
+ 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(Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, ClickType clickType, 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_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/BeaconInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/BeaconInventoryImpl.java
new file mode 100644
index 000000000..0b5a92b30
--- /dev/null
+++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/BeaconInventoryImpl.java
@@ -0,0 +1,161 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftBeaconView;
+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.21.1
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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 CraftBeaconView 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 CraftBeaconView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryBeacon inventory = new CraftInventoryBeacon(this.inputSlot);
+
+ this.bukkitEntity = new CraftBeaconView(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_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/CartographyTableInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..12fd766e1
--- /dev/null
+++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/CartographyTableInventoryImpl.java
@@ -0,0 +1,196 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_21_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.21.1
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/EnchantingTableInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..ccf3c367c
--- /dev/null
+++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/EnchantingTableInventoryImpl.java
@@ -0,0 +1,180 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftEnchantmentView;
+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.21.1
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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 CraftEnchantmentView 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 CraftEnchantmentView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.inputSlots);
+
+ this.bukkitEntity = new CraftEnchantmentView(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_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/GrindstoneInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..143e87373
--- /dev/null
+++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_21_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.21.1
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/MerchantInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/MerchantInventoryImpl.java
new file mode 100644
index 000000000..5792119fb
--- /dev/null
+++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/MerchantInventoryImpl.java
@@ -0,0 +1,321 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_1.util.TextHolderUtil;
+import net.minecraft.core.component.DataComponentPredicate;
+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.ItemCost;
+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_21_R1.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftMerchantView;
+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;
+import java.util.Optional;
+
+/**
+ * Internal merchant inventory for 1.21.1
+ *
+ * @since 0.10.18
+ */
+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);
+ }
+
+ ItemCost itemCostA = convertItemStackToItemCost(nmsItemA);
+ ItemCost itemCostB = convertItemStackToItemCost(nmsItemB);
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ itemCostA, Optional.of(itemCostB), 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);
+ }
+
+ /**
+ * Converts an NMS item stack to an item cost.
+ *
+ * @param itemStack the item stack to convert
+ * @return the item cost
+ * @since 0.10.18
+ */
+ @NotNull
+ @Contract(value = "_ -> new", pure = true)
+ private ItemCost convertItemStackToItemCost(@NotNull net.minecraft.world.item.ItemStack itemStack) {
+ DataComponentPredicate predicate = DataComponentPredicate.allOf(itemStack.getComponents());
+
+ return new ItemCost(itemStack.getItemHolder(), itemStack.getCount(), predicate, itemStack);
+ }
+
+ /**
+ * 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.18
+ */
+ @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.18
+ */
+ @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 CraftMerchantView 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 CraftMerchantView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryMerchant inventory = new CraftInventoryMerchant(this.merchant, this.container);
+
+ this.bukkitEntity = new CraftMerchantView(this.humanEntity, inventory, this, this.merchant);
+
+ 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_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/SmithingTableInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..d16b1dada
--- /dev/null
+++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/SmithingTableInventoryImpl.java
@@ -0,0 +1,217 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_21_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.21.1. This is only available for Minecraft 1.20 and higher.
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/StonecutterInventoryImpl.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..fb07889e6
--- /dev/null
+++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/StonecutterInventoryImpl.java
@@ -0,0 +1,200 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_1;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_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_21_R1.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftStonecutterView;
+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.21.1
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ 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 CraftStonecutterView 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 CraftStonecutterView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryStonecutter inventory = new CraftInventoryStonecutter(this.inputSlot, this.resultSlot);
+
+ this.bukkitEntity = new CraftStonecutterView(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_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/CustomInventoryUtil.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..e1d0af6d9
--- /dev/null
+++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_1.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_21_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.18
+ */
+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.18
+ */
+ @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_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/TextHolderUtil.java b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/TextHolderUtil.java
new file mode 100644
index 000000000..16d0416fd
--- /dev/null
+++ b/nms/1_21_1/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_1/util/TextHolderUtil.java
@@ -0,0 +1,67 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_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.core.HolderLookup;
+import net.minecraft.network.chat.Component;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/**
+ * A utility class for adding {@link TextHolder} support.
+ *
+ * @since 0.10.18
+ */
+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.18
+ */
+ @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.18
+ */
+ @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.18
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull ComponentHolder holder) {
+ return Objects.requireNonNull(Component.Serializer.fromJson(holder.asJson(), HolderLookup.Provider.create(Stream.empty())));
+ }
+}
diff --git a/nms/1_21_11/pom.xml b/nms/1_21_11/pom.xml
new file mode 100644
index 000000000..5bdf031da
--- /dev/null
+++ b/nms/1_21_11/pom.xml
@@ -0,0 +1,72 @@
+
+
+ 4.0.0
+
+ com.github.stefvanschie.inventoryframework
+ IF-parent
+ 0.12.0-SNAPSHOT
+ ../../pom.xml
+
+
+ 1_21_11
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ org.spigotmc
+ spigot
+ 1.21.11-R0.1-SNAPSHOT
+ remapped-mojang
+ provided
+
+
+
+
+
+
+ net.md-5
+ specialsource-maven-plugin
+ 2.0.4
+
+
+ package
+
+ remap
+
+ remap-obf
+
+ org.spigotmc:minecraft-server:1.21.11-R0.1-SNAPSHOT:txt:maps-mojang
+ true
+ org.spigotmc:spigot:1.21.11-R0.1-SNAPSHOT:jar:remapped-mojang
+ true
+ remapped-obf
+
+
+
+ package
+
+ remap
+
+ remap-spigot
+
+ ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar
+ org.spigotmc:minecraft-server:1.21.11-R0.1-SNAPSHOT:csrg:maps-spigot
+ org.spigotmc:spigot:1.21.11-R0.1-SNAPSHOT:jar:remapped-obf
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/AnvilInventoryImpl.java b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/AnvilInventoryImpl.java
new file mode 100644
index 000000000..1ad863f45
--- /dev/null
+++ b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/AnvilInventoryImpl.java
@@ -0,0 +1,229 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_11;
+
+import com.github.stefvanschie.inventoryframework.abstraction.AnvilInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_11.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_21_R7.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_21_R7.inventory.view.CraftAnvilView;
+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.21.11
+ *
+ * @since 0.11.6
+ */
+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) {
+ @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.6
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {}
+
+ /**
+ * A custom container anvil for responding to item renaming
+ *
+ * @since 0.11.6
+ */
+ 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 CraftAnvilView 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.6
+ */
+ 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);
+ }
+
+ @Override
+ public CraftAnvilView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(
+ this.access.getLocation(),
+ this.inputSlots,
+ this.resultSlot
+ );
+
+ this.bukkitEntity = new CraftAnvilView(this.player.getBukkitEntity(), inventory, this);
+ this.bukkitEntity.updateFromLegacy(inventory);
+
+ return this.bukkitEntity;
+ }
+
+ @Override
+ public void broadcastChanges() {
+ if (super.cost.checkAndClearUpdateFlag()) {
+ broadcastFullState();
+ } else {
+ for (int index = 0; index < super.slots.size(); index++) {
+ if (!super.remoteSlots.get(index).matches(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(Container container) {
+ broadcastChanges();
+ }
+
+ @Override
+ public void clicked(int index, int dragData, ClickType clickType, 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.6
+ */
+ 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_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/BeaconInventoryImpl.java b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/BeaconInventoryImpl.java
new file mode 100644
index 000000000..ca1dd5545
--- /dev/null
+++ b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/BeaconInventoryImpl.java
@@ -0,0 +1,161 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_11;
+
+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_21_R7.inventory.CraftInventoryBeacon;
+import org.bukkit.craftbukkit.v1_21_R7.inventory.view.CraftBeaconView;
+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.21.11
+ *
+ * @since 0.11.6
+ */
+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.6
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with one slot.
+ *
+ * @since 0.11.6
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container beacon.
+ *
+ * @since 0.11.6
+ */
+ 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 CraftBeaconView 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.6
+ */
+ 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 CraftBeaconView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryBeacon inventory = new CraftInventoryBeacon(this.inputSlot);
+
+ this.bukkitEntity = new CraftBeaconView(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_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/CartographyTableInventoryImpl.java b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/CartographyTableInventoryImpl.java
new file mode 100644
index 000000000..ec39bff17
--- /dev/null
+++ b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/CartographyTableInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_11;
+
+import com.github.stefvanschie.inventoryframework.abstraction.CartographyTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_11.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_21_R7.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_21_R7.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.21.11
+ *
+ * @since 0.11.6
+ */
+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.6
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.6
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container cartography table.
+ *
+ * @since 0.11.6
+ */
+ 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.6
+ */
+ 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.6
+ */
+ 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_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/EnchantingTableInventoryImpl.java b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/EnchantingTableInventoryImpl.java
new file mode 100644
index 000000000..19e25f7d6
--- /dev/null
+++ b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/EnchantingTableInventoryImpl.java
@@ -0,0 +1,175 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_11;
+
+import com.github.stefvanschie.inventoryframework.abstraction.EnchantingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_11.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_21_R7.inventory.CraftInventoryEnchanting;
+import org.bukkit.craftbukkit.v1_21_R7.inventory.view.CraftEnchantmentView;
+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;
+
+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.6
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.6
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container enchanting table.
+ *
+ * @since 0.11.6
+ */
+ 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 CraftEnchantmentView 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.6
+ */
+ 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 CraftEnchantmentView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.inputSlots);
+
+ this.bukkitEntity = new CraftEnchantmentView(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.6
+ */
+ 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_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/GrindstoneInventoryImpl.java b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/GrindstoneInventoryImpl.java
new file mode 100644
index 000000000..a9f0fb3ea
--- /dev/null
+++ b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/GrindstoneInventoryImpl.java
@@ -0,0 +1,195 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_11;
+
+import com.github.stefvanschie.inventoryframework.abstraction.GrindstoneInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_11.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_21_R7.inventory.CraftInventoryGrindstone;
+import org.bukkit.craftbukkit.v1_21_R7.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.21.11
+ *
+ * @since 0.11.6
+ */
+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.6
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with two slots.
+ *
+ * @since 0.11.6
+ */
+ public InventoryViewProvider() {
+ super(2);
+ }
+ }
+
+ /**
+ * A custom container grindstone.
+ *
+ * @since 0.11.6
+ */
+ 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.6
+ */
+ 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.6
+ */
+ 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_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/MerchantInventoryImpl.java b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/MerchantInventoryImpl.java
new file mode 100644
index 000000000..d9fbaddd1
--- /dev/null
+++ b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/MerchantInventoryImpl.java
@@ -0,0 +1,321 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_11;
+
+import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_11.util.TextHolderUtil;
+import net.minecraft.core.component.DataComponentExactPredicate;
+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.entity.player.Player;
+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.ItemCost;
+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_21_R7.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_21_R7.inventory.CraftInventoryMerchant;
+import org.bukkit.craftbukkit.v1_21_R7.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.v1_21_R7.inventory.view.CraftMerchantView;
+import org.bukkit.entity.HumanEntity;
+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;
+import java.util.Optional;
+
+/**
+ * Internal merchant inventory for 1.21.11
+ *
+ * @since 0.11.6
+ */
+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 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 org.bukkit.entity.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);
+ }
+
+ ItemCost itemCostA = convertItemStackToItemCost(nmsItemA);
+ ItemCost itemCostB = convertItemStackToItemCost(nmsItemB);
+
+ int uses = recipe.getUses();
+ int maxUses = recipe.getMaxUses();
+ int exp = recipe.getVillagerExperience();
+ float multiplier = recipe.getPriceMultiplier();
+
+ MerchantOffer merchantOffer = new MerchantOffer(
+ itemCostA, Optional.of(itemCostB), 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);
+ }
+
+ /**
+ * Converts an NMS item stack to an item cost.
+ *
+ * @param itemStack the item stack to convert
+ * @return the item cost
+ * @since 0.11.6
+ */
+ @NotNull
+ @Contract(value = "_ -> new", pure = true)
+ private ItemCost convertItemStackToItemCost(@NotNull net.minecraft.world.item.ItemStack itemStack) {
+ DataComponentExactPredicate predicate = DataComponentExactPredicate.allOf(itemStack.getComponents());
+
+ return new ItemCost(itemStack.getItemHolder(), itemStack.getCount(), predicate, itemStack);
+ }
+
+ /**
+ * Gets the server player associated to this player
+ *
+ * @param player the player to get the server player from
+ * @return the server player
+ * @since 0.11.6
+ */
+ @NotNull
+ @Contract(pure = true)
+ private ServerPlayer getServerPlayer(@NotNull org.bukkit.entity.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.11.6
+ */
+ @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.6
+ */
+ 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.6
+ */
+ public InventoryViewProvider(@NotNull Merchant merchant) {
+ super(merchant);
+ }
+ }
+
+ /**
+ * A custom container merchant
+ *
+ * @since 0.11.6
+ */
+ 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 CraftMerchantView 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.6
+ */
+ public ContainerMerchantImpl(
+ int containerId,
+ @NotNull 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 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 CraftMerchantView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryMerchant inventory = new CraftInventoryMerchant(this.merchant, this.container);
+
+ this.bukkitEntity = new CraftMerchantView(this.humanEntity, inventory, this, this.merchant);
+
+ 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) {}
+
+ @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.6
+ */
+ 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_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/SmithingTableInventoryImpl.java b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/SmithingTableInventoryImpl.java
new file mode 100644
index 000000000..d28c4254d
--- /dev/null
+++ b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/SmithingTableInventoryImpl.java
@@ -0,0 +1,217 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_11;
+
+import com.github.stefvanschie.inventoryframework.abstraction.SmithingTableInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_11.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_21_R7.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_21_R7.inventory.CraftInventorySmithing;
+import org.bukkit.craftbukkit.v1_21_R7.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.21.11. This is only available for Minecraft 1.20 and higher.
+ *
+ * @since 0.11.6
+ */
+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.6
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.6
+ */
+ public InventoryViewProvider() {
+ super(3);
+ }
+ }
+
+ /**
+ * A custom container smithing table
+ *
+ * @since 0.11.6
+ */
+ 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.6
+ */
+ public ContainerSmithingTableImpl(
+ int containerId,
+ @NotNull 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.6
+ */
+ 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_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/StonecutterInventoryImpl.java b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/StonecutterInventoryImpl.java
new file mode 100644
index 000000000..6f3dd2957
--- /dev/null
+++ b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/StonecutterInventoryImpl.java
@@ -0,0 +1,203 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_11;
+
+import com.github.stefvanschie.inventoryframework.abstraction.StonecutterInventory;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.github.stefvanschie.inventoryframework.nms.v1_21_11.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.Slot;
+import net.minecraft.world.inventory.StonecutterMenu;
+import org.bukkit.craftbukkit.v1_21_R7.inventory.CraftInventoryStonecutter;
+import org.bukkit.craftbukkit.v1_21_R7.inventory.view.CraftStonecutterView;
+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.21.11
+ *
+ * @since 0.11.6
+ */
+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.6
+ */
+ private abstract static class InventoryViewProvider extends SimpleContainer implements MenuProvider {
+
+ /**
+ * Creates a new inventory view provider with three slots.
+ *
+ * @since 0.11.6
+ */
+ public InventoryViewProvider() {
+ super(1);
+ }
+ }
+
+ /**
+ * A custom container stonecutter
+ *
+ * @since 0.11.6
+ */
+ 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 CraftStonecutterView 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.6
+ */
+ public ContainerStonecutterImpl(
+ int containerId,
+ @NotNull 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 CraftStonecutterView getBukkitView() {
+ if (this.bukkitEntity != null) {
+ return this.bukkitEntity;
+ }
+
+ CraftInventoryStonecutter inventory = new CraftInventoryStonecutter(this.inputSlot, this.resultSlot);
+
+ this.bukkitEntity = new CraftStonecutterView(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 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.6
+ */
+ 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_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/util/CustomInventoryUtil.java b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/util/CustomInventoryUtil.java
new file mode 100644
index 000000000..251e2ad01
--- /dev/null
+++ b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/util/CustomInventoryUtil.java
@@ -0,0 +1,41 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_11.util;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_21_R7.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.11.6
+ */
+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.11.6
+ */
+ @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_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/util/TextHolderUtil.java b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/util/TextHolderUtil.java
new file mode 100644
index 000000000..20e2d3199
--- /dev/null
+++ b/nms/1_21_11/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_21_11/util/TextHolderUtil.java
@@ -0,0 +1,76 @@
+package com.github.stefvanschie.inventoryframework.nms.v1_21_11.util;
+
+import com.github.stefvanschie.inventoryframework.adventuresupport.ComponentHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.StringHolder;
+import com.github.stefvanschie.inventoryframework.adventuresupport.TextHolder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.JsonOps;
+import net.minecraft.core.HolderLookup;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.ComponentSerialization;
+import net.minecraft.resources.RegistryOps;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.stream.Stream;
+
+/**
+ * A utility class for adding {@link TextHolder} support.
+ *
+ * @since 0.11.6
+ */
+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.11.6
+ */
+ @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.11.6
+ */
+ @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.11.6
+ */
+ @NotNull
+ @Contract(pure = true)
+ private static Component toComponent(@NotNull ComponentHolder holder) {
+ Codec extends Component> codec = ComponentSerialization.CODEC;
+ HolderLookup.Provider provider = HolderLookup.Provider.create(Stream.empty());
+ RegistryOps super JsonElement> serializationContext = provider.createSerializationContext(JsonOps.INSTANCE);
+
+ return codec.parse(serializationContext, holder.asJson()).getOrThrow(JsonParseException::new);
+ }
+}
diff --git a/nms/1_21_2-3/pom.xml b/nms/1_21_2-3/pom.xml
new file mode 100644
index 000000000..43f6977dc
--- /dev/null
+++ b/nms/1_21_2-3/pom.xml
@@ -0,0 +1,72 @@
+
+
+ 4.0.0
+
+ com.github.stefvanschie.inventoryframework
+ IF-parent
+ 0.12.0-SNAPSHOT
+ ../../pom.xml
+
+
+ 1_21_2-3
+
+
+ true
+
+
+
+
+ com.github.stefvanschie.inventoryframework
+ abstraction
+ ${project.version}
+ compile
+
+
+ org.spigotmc
+ spigot
+ 1.21.3-R0.1-SNAPSHOT
+ remapped-mojang
+ provided
+
+
+
+
+
+
+ net.md-5
+ specialsource-maven-plugin
+ 2.0.4
+
+
+ package
+
+