diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 671a7535..fc422adf 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -92,6 +92,10 @@ updates: directory: "/nms/1_20_5" schedule: interval: "daily" + - package-ecosystem: "maven" + directory: "/nms/1_20_6" + schedule: + interval: "daily" - package-ecosystem: "maven" directory: "/nms/abstraction" schedule: diff --git a/.github/workflows/auto-deploy.yml b/.github/workflows/auto-deploy.yml index 0eda366c..07a0ae63 100644 --- a/.github/workflows/auto-deploy.yml +++ b/.github/workflows/auto-deploy.yml @@ -75,7 +75,7 @@ jobs: mvn paper-nms:init -pl nms/1_20_1 mvn paper-nms:init -pl nms/1_20_2 mvn paper-nms:init -pl nms/1_20_3-4 - - name: Run BuildTools 1.20.5 + - name: 'Run BuildTools 1.20.5 & 1.20.6' if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' run: | mkdir -p build @@ -103,6 +103,7 @@ jobs: cd .. java -jar BuildTools.jar --remapped --disable-java-check --dont-update + java -jar BuildTools.jar --rev 1.20.6 --remapped --disable-java-check cd ../ - name: Setup GPG keys run: cat <(echo -e "${{ secrets.GPG_KEY }}") | gpg --batch --import diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index e5ee2553..3ece9364 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -81,7 +81,7 @@ jobs: mvn paper-nms:init -pl nms/1_20_1 mvn paper-nms:init -pl nms/1_20_2 mvn paper-nms:init -pl nms/1_20_3-4 - - name: Run BuildTools 1.20.5 + - name: 'Run BuildTools 1.20.5 & 1.20.6' if: steps.cache-paperclip-jars.outputs.cache-hit != 'true' run: | mkdir -p build @@ -109,6 +109,7 @@ jobs: cd .. java -jar BuildTools.jar --remapped --disable-java-check --dont-update + java -jar BuildTools.jar --rev 1.20.6 --remapped --disable-java-check cd ../ - name: Build with Maven run: mvn -B package --file pom.xml diff --git a/IF/pom.xml b/IF/pom.xml index 158faa0e..1ef5bdd1 100644 --- a/IF/pom.xml +++ b/IF/pom.xml @@ -161,6 +161,12 @@ ${project.version} compile + + com.github.stefvanschie.inventoryframework + 1_20_6 + ${project.version} + compile + org.spigotmc spigot-api 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 32ce41d7..64568eae 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 @@ -153,14 +153,21 @@ public enum Version { * * @since 0.10.14 */ - V1_20_5; + V1_20_5, + + /** + * Version 1.20.6 + * + * @since 0.10.14 + */ + V1_20_6; /** * 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_0, V1_20_1, V1_20_2, V1_20_3_4, V1_20_5, V1_20_6 ); /** @@ -259,6 +266,8 @@ public static Version getVersion() { return V1_20_3_4; case "1.20.5": return V1_20_5; + case "1.20.6": + return V1_20_6; 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 39606800..f793eea1 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 @@ -315,6 +315,8 @@ public static StonecutterInventory newStonecutterInventory(@NotNull Version vers 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); BEACON_INVENTORIES = new EnumMap<>(Version.class); BEACON_INVENTORIES.put(Version.V1_14, @@ -357,6 +359,8 @@ public static StonecutterInventory newStonecutterInventory(@NotNull Version vers 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); CARTOGRAPHY_TABLE_INVENTORIES = new EnumMap<>(Version.class); CARTOGRAPHY_TABLE_INVENTORIES.put(Version.V1_14, @@ -399,6 +403,8 @@ public static StonecutterInventory newStonecutterInventory(@NotNull Version vers 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); ENCHANTING_TABLE_INVENTORIES = new EnumMap<>(Version.class); ENCHANTING_TABLE_INVENTORIES.put(Version.V1_14, @@ -441,6 +447,8 @@ public static StonecutterInventory newStonecutterInventory(@NotNull Version vers 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); GRINDSTONE_INVENTORIES = new EnumMap<>(Version.class); GRINDSTONE_INVENTORIES.put(Version.V1_14, @@ -483,6 +491,8 @@ public static StonecutterInventory newStonecutterInventory(@NotNull Version vers 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); MERCHANT_INVENTORIES = new EnumMap<>(Version.class); MERCHANT_INVENTORIES.put(Version.V1_14, @@ -525,6 +535,8 @@ public static StonecutterInventory newStonecutterInventory(@NotNull Version vers 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); SMITHING_TABLE_INVENTORIES = new EnumMap<>(Version.class); SMITHING_TABLE_INVENTORIES.put(Version.V1_19_4, @@ -539,6 +551,8 @@ public static StonecutterInventory newStonecutterInventory(@NotNull Version vers 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); LEGACY_SMITHING_TABLE_INVENTORIES = new EnumMap<>(Version.class); LEGACY_SMITHING_TABLE_INVENTORIES.put(Version.V1_16_1, @@ -609,5 +623,7 @@ public static StonecutterInventory newStonecutterInventory(@NotNull Version vers 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); } } diff --git a/nms/1_20_6/pom.xml b/nms/1_20_6/pom.xml new file mode 100644 index 00000000..c5dd0062 --- /dev/null +++ b/nms/1_20_6/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + com.github.stefvanschie.inventoryframework + IF-parent + 0.10.14-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.3 + + + 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 00000000..65cfec65 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/AnvilInventoryImpl.java @@ -0,0 +1,284 @@ +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.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.AnvilMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +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.20.6 + * + * @since 0.10.14 + */ +public class AnvilInventoryImpl extends AnvilInventory { + + public AnvilInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder 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 + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerAnvilImpl containerAnvil = new ContainerAnvilImpl(serverPlayer, message); + + Inventory inventory = containerAnvil.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerAnvil.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, message)); + serverPlayer.containerMenu = containerAnvil; + serverPlayer.initMenu(containerAnvil); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 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.EMPTY); + } + + @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) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * 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.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -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.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * 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(); + } + + /** + * A custom container anvil for responding to item renaming + * + * @since 0.10.14 + */ + private class ContainerAnvilImpl extends AnvilMenu { + + /** + * Creates a new custom anvil container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.14 + */ + public ContainerAnvilImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + this.checkReachable = false; + this.cost.set(AnvilInventoryImpl.super.cost); + + setTitle(title); + + Slot originalSlot = this.slots.get(2); + + this.slots.set(2, new Slot(originalSlot.container, originalSlot.index, originalSlot.x, originalSlot.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + originalSlot.onTake(player, stack); + } + }); + } + + @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 createResult() {} + + @Override + public void removed(net.minecraft.world.entity.player.@NotNull Player nmsPlayer) {} + + @Override + protected void clearContainer(net.minecraft.world.entity.player.@NotNull Player player, + @NotNull Container inventory) {} + + @Override + protected void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) {} + + public int getContainerId() { + return this.containerId; + } + } +} 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 00000000..8d2b3914 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/BeaconInventoryImpl.java @@ -0,0 +1,200 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6; + +import com.github.stefvanschie.inventoryframework.abstraction.BeaconInventory; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.BeaconMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.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.20.6 + * + * @since 0.10.14 + */ +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) { + ServerPlayer serverPlayer = getServerPlayer(player); + ContainerBeaconImpl containerBeacon = new ContainerBeaconImpl(serverPlayer, item); + + serverPlayer.containerMenu = containerBeacon; + + int id = containerBeacon.containerId; + Component beacon = Component.literal("Beacon"); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.BEACON, beacon)); + + sendItem(player, item); + } + + @Override + public void sendItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + NonNullList items = NonNullList.of( + ItemStack.EMPTY, //the first item doesn't count for some reason, so send a dummy item + CraftItemStack.asNMSCopy(item) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, items, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * 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(); + } + + /** + * A custom container beacon + * + * @since 0.10.14 + */ + private class ContainerBeaconImpl extends BeaconMenu { + + /** + * 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 ServerPlayer serverPlayer, @Nullable org.bukkit.inventory.ItemStack item) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + setTitle(Component.empty()); + + try { + //noinspection JavaReflectionMemberAccess + this.beaconField = BeaconMenu.class.getDeclaredField("s"); //beacon + this.beaconField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + ItemStack itemStack = CraftItemStack.asNMSCopy(item); + + ((Container) 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((Container) 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 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) {} + + } +} 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 00000000..0e9624f2 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/CartographyTableInventoryImpl.java @@ -0,0 +1,218 @@ +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.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CartographyTableMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryCartography; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.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.20.6 + * + * @since 0.10.14 + */ +public class CartographyTableInventoryImpl extends CartographyTableInventory { + + public CartographyTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder 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 + "'" + ); + } + + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + + ContainerCartographyTableImpl containerCartographyTable = new ContainerCartographyTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerCartographyTable; + + int id = containerCartographyTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.CARTOGRAPHY_TABLE, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * 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(); + } + + /** + * A custom container cartography table + * + * @since 0.10.14 + */ + private class ContainerCartographyTableImpl extends CartographyTableMenu { + + /** + * 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 resultContainerField; + + public ContainerCartographyTableImpl(@NotNull ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = CartographyTableMenu.class.getDeclaredField("u"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.setItem(0, CraftItemStack.asNMSCopy(items[0])); + container.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.container, 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 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) {} + + @NotNull + @Contract(pure = true) + private Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + + } +} 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 00000000..d3a65c70 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/EnchantingTableInventoryImpl.java @@ -0,0 +1,218 @@ +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.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.EnchantmentMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.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.20.6 + * + * @since 0.10.14 + */ +public class EnchantingTableInventoryImpl extends EnchantingTableInventory { + + public EnchantingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder 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 + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerEnchantingTableImpl containerEnchantmentTable = new ContainerEnchantingTableImpl( + serverPlayer, items, message + ); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(id, MenuType.ENCHANTMENT, message)); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * 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; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * 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(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.14 + */ + private class ContainerEnchantingTableImpl extends EnchantmentMenu { + + /** + * 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 ServerPlayer serverPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, + @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + this.player = serverPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.enchantSlotsField = EnchantmentMenu.class.getDeclaredField("o"); //enchantSlots + this.enchantSlotsField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + try { + Container input = (Container) 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((Container) 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 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) {} + + } +} 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 00000000..04f79f89 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/GrindstoneInventoryImpl.java @@ -0,0 +1,208 @@ +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.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.TextHolderUtil; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +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 { + + public GrindstoneInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder 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 + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerGrindstoneImpl containerGrindstone = new ContainerGrindstoneImpl(serverPlayer, message); + + Inventory inventory = containerGrindstone.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + + int containerId = containerGrindstone.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.GRINDSTONE, message)); + serverPlayer.containerMenu = containerGrindstone; + serverPlayer.initMenu(containerGrindstone); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + if (cursor == null) { + throw new IllegalArgumentException("Cursor may not be null on version 1.19.2"); + } + + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * 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 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * 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(); + } + + /** + * A custom container grindstone + * + * @since 0.10.14 + */ + private static class ContainerGrindstoneImpl extends GrindstoneMenu { + + /** + * Creates a new grindstone container + * + * @param serverPlayer the player for whom this container should be opened + * @param title the title of the gui + * @since 0.10.14 + */ + public ContainerGrindstoneImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory()); + + setTitle(title); + + Slot firstSlot = this.slots.get(0); + Slot secondSlot = this.slots.get(1); + Slot thirdSlot = this.slots.get(2); + + this.slots.set(0, new Slot(firstSlot.container, firstSlot.index, firstSlot.x, firstSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(1, new Slot(secondSlot.container, secondSlot.index, secondSlot.x, secondSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + }); + + this.slots.set(2, new Slot(thirdSlot.container, thirdSlot.index, thirdSlot.x, thirdSlot.y) { + @Override + public boolean mayPlace(ItemStack stack) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {} + }); + } + + @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) {} + + public int getContainerId() { + return this.containerId; + } + } +} 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 00000000..4ec8a905 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/MerchantInventoryImpl.java @@ -0,0 +1,118 @@ +package com.github.stefvanschie.inventoryframework.nms.v1_20_6; + +import com.github.stefvanschie.inventoryframework.abstraction.MerchantInventory; +import net.minecraft.core.component.DataComponentPredicate; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.trading.ItemCost; +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.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +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 { + + @Override + public void sendMerchantOffers(@NotNull Player player, + @NotNull List> trades, + int level, int experience) { + MerchantOffers offers = new MerchantOffers(); + + for (Map.Entry 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; + } +} 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 00000000..145f05e9 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/SmithingTableInventoryImpl.java @@ -0,0 +1,335 @@ +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.CustomInventoryUtil; +import com.github.stefvanschie.inventoryframework.nms.v1_20_6.util.TextHolderUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +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.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +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.20.6. This is only available for Minecraft 1.20 and higher. + * + * @since 0.10.14 + */ +public class SmithingTableInventoryImpl extends SmithingTableInventory { + + public SmithingTableInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @NotNull + @Override + public Inventory openInventory(@NotNull Player player, @NotNull TextHolder title, + @Nullable org.bukkit.inventory.ItemStack[] items) { + int itemAmount = items.length; + + if (itemAmount != 4) { + throw new IllegalArgumentException( + "The amount of items for a smithing table should be 4, but is '" + itemAmount + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + + //ignore deprecation: superseding method is only available on Paper + //noinspection deprecation + CraftEventFactory.handleInventoryCloseEvent(serverPlayer); + + serverPlayer.containerMenu = serverPlayer.inventoryMenu; + + Component message = TextHolderUtil.toComponent(title); + ContainerSmithingTableImpl containerSmithingTable = new ContainerSmithingTableImpl(serverPlayer, message); + + Inventory inventory = containerSmithingTable.getBukkitView().getTopInventory(); + + inventory.setItem(0, items[0]); + inventory.setItem(1, items[1]); + inventory.setItem(2, items[2]); + inventory.setItem(3, items[3]); + + int containerId = containerSmithingTable.getContainerId(); + + serverPlayer.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.SMITHING, message)); + serverPlayer.containerMenu = containerSmithingTable; + serverPlayer.initMenu(containerSmithingTable); + + return inventory; + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items, + @Nullable org.bukkit.inventory.ItemStack cursor) { + NonNullList nmsItems = CustomInventoryUtil.convertToNMSItems(items); + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack nmsCursor = CraftItemStack.asNMSCopy(cursor); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, nmsCursor)); + } + + @Override + public void sendFirstItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 0, nmsItem)); + } + + @Override + public void sendSecondItem(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + ItemStack nmsItem = CraftItemStack.asNMSCopy(item); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 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.EMPTY); + } + + @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) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * 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.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void setCursor(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -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.10.14 + * @deprecated no longer used internally + */ + @Deprecated + private void sendResultItem(@NotNull Player player, @NotNull ItemStack item) { + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(containerId, state, 2, item)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + * @deprecated no longer used internally + */ + @Contract(pure = true) + @Deprecated + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + * @deprecated no longer used internally + */ + @NotNull + @Contract(pure = true) + @Deprecated + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * 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(); + } + + /** + * A custom container smithing table + * + * @since 0.10.14 + */ + private static class ContainerSmithingTableImpl extends SmithingMenu { + + /** + * Creates a new custom smithing table container for the specified player + * + * @param serverPlayer the player for whom this anvil container is + * @param title the title of the inventory + * @since 0.10.14 + */ + public ContainerSmithingTableImpl(@NotNull ServerPlayer serverPlayer, @NotNull Component title) { + super(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), + ContainerLevelAccess.create(serverPlayer.getCommandSenderWorld(), new BlockPos(0, 0, 0))); + + setTitle(title); + + this.checkReachable = false; + + Slot slotOne = this.slots.get(0); + Slot slotTwo = this.slots.get(1); + Slot slotThree = this.slots.get(2); + Slot slotFour = this.slots.get(3); + + this.slots.set(0, new Slot(slotOne.container, slotOne.index, slotOne.x, slotOne.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotOne.onTake(player, stack); + } + }); + + this.slots.set(1, new Slot(slotTwo.container, slotTwo.index, slotTwo.x, slotTwo.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotTwo.onTake(player, stack); + } + }); + + this.slots.set(2, new Slot(slotThree.container, slotThree.index, slotThree.x, slotThree.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotThree.onTake(player, stack); + } + }); + + this.slots.set(3, new Slot(slotFour.container, slotFour.index, slotFour.x, slotFour.y) { + @Override + public boolean mayPlace(@NotNull ItemStack stack) { + return true; + } + + @Override + public boolean mayPickup(net.minecraft.world.entity.player.@NotNull Player playerEntity) { + return true; + } + + @Override + public void onTake(net.minecraft.world.entity.player.@NotNull Player player, @NotNull ItemStack stack) { + slotFour.onTake(player, stack); + } + }); + } + + @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; + } + + public int getContainerId() { + return this.containerId; + } + } +} 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 00000000..62b21ba9 --- /dev/null +++ b/nms/1_20_6/src/main/java/com/github/stefvanschie/inventoryframework/nms/v1_20_6/StonecutterInventoryImpl.java @@ -0,0 +1,221 @@ +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.NonNullList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.StonecutterMenu; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryStonecutter; +import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_20_R4.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.20.6 + * + * @since 0.10.14 + */ +public class StonecutterInventoryImpl extends StonecutterInventory { + + public StonecutterInventoryImpl(@NotNull InventoryHolder inventoryHolder) { + super(inventoryHolder); + } + + @Override + public void openInventory(@NotNull Player player, @NotNull TextHolder 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 + "'" + ); + } + + ServerPlayer serverPlayer = getServerPlayer(player); + Component message = TextHolderUtil.toComponent(title); + ContainerStonecutterImpl containerEnchantmentTable = new ContainerStonecutterImpl(serverPlayer, items, message); + + serverPlayer.containerMenu = containerEnchantmentTable; + + int id = containerEnchantmentTable.containerId; + ClientboundOpenScreenPacket packet = new ClientboundOpenScreenPacket(id, MenuType.STONECUTTER, message); + + serverPlayer.connection.send(packet); + + sendItems(player, items); + } + + @Override + public void sendItems(@NotNull Player player, @Nullable org.bukkit.inventory.ItemStack[] items) { + NonNullList nmsItems = NonNullList.of( + ItemStack.EMPTY, + CraftItemStack.asNMSCopy(items[0]), + CraftItemStack.asNMSCopy(items[1]) + ); + + ServerPlayer serverPlayer = getServerPlayer(player); + int containerId = getContainerId(serverPlayer); + int state = serverPlayer.containerMenu.incrementStateId(); + ItemStack cursor = CraftItemStack.asNMSCopy(player.getItemOnCursor()); + ServerPlayerConnection playerConnection = getPlayerConnection(serverPlayer); + + playerConnection.send(new ClientboundContainerSetContentPacket(containerId, state, nmsItems, cursor)); + } + + @Override + public void clearCursor(@NotNull Player player) { + ServerPlayer serverPlayer = getServerPlayer(player); + int state = serverPlayer.containerMenu.incrementStateId(); + + getPlayerConnection(serverPlayer).send(new ClientboundContainerSetSlotPacket(-1, state, -1, ItemStack.EMPTY)); + } + + /** + * Gets the container id for the inventory view the player currently has open + * + * @param nmsPlayer the player to get the container id for + * @return the container id + * @since 0.10.14 + */ + @Contract(pure = true) + private int getContainerId(@NotNull net.minecraft.world.entity.player.Player nmsPlayer) { + return nmsPlayer.containerMenu.containerId; + } + + /** + * Gets the player connection for the specified player + * + * @param serverPlayer the player to get the player connection from + * @return the player connection + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + private ServerPlayerConnection getPlayerConnection(@NotNull ServerPlayer serverPlayer) { + return serverPlayer.connection; + } + + /** + * 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(); + } + + /** + * A custom container enchanting table + * + * @since 0.10.14 + */ + private class ContainerStonecutterImpl extends StonecutterMenu { + + /** + * 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 resultContainerField; + + public ContainerStonecutterImpl(@NotNull ServerPlayer entityPlayer, + @Nullable org.bukkit.inventory.ItemStack[] items, @NotNull Component title) { + super(entityPlayer.nextContainerCounter(), entityPlayer.getInventory()); + + this.player = entityPlayer.getBukkitEntity(); + + setTitle(title); + + try { + //noinspection JavaReflectionMemberAccess + this.resultContainerField = StonecutterMenu.class.getDeclaredField("A"); //resultContainer + this.resultContainerField.setAccessible(true); + } catch (NoSuchFieldException exception) { + throw new RuntimeException(exception); + } + + container.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.container, 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 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) {} + + /** + * Gets the result inventory + * + * @return the result inventory + * @since 0.10.14 + */ + @NotNull + @Contract(pure = true) + public Container getResultInventory() { + try { + return (Container) resultContainerField.get(this); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } + } +} 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 00000000..a0f724f1 --- /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 00000000..8e945b8b --- /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/pom.xml b/pom.xml index 6bd6e3ec..673bef24 100644 --- a/pom.xml +++ b/pom.xml @@ -7,6 +7,7 @@ IF nms/abstraction + nms/1_20_6 nms/1_20_5 nms/1_20_3-4 nms/1_20_2