From f7755aead7885a489dc5042e4facfc0edf46c2d0 Mon Sep 17 00:00:00 2001
From: NichtStudioCode <51272202+NichtStudioCode@users.noreply.github.com>
Date: Mon, 12 Aug 2024 13:37:37 +0200
Subject: [PATCH] 1.21.1 Support
---
README.md | 2 +-
inventoryaccess/inventory-access-r21/pom.xml | 150 ++++++++++++++
.../r21/AnvilInventoryImpl.java | 188 ++++++++++++++++++
.../r21/CartographyInventoryImpl.java | 136 +++++++++++++
.../r21/InventoryUtilsImpl.java | 80 ++++++++
.../inventoryaccess/r21/ItemUtilsImpl.java | 104 ++++++++++
.../inventoryaccess/r21/PlayerUtilsImpl.java | 116 +++++++++++
.../version/InventoryAccessRevision.java | 1 +
invui/pom.xml | 5 +
pom.xml | 1 +
10 files changed, 782 insertions(+), 1 deletion(-)
create mode 100644 inventoryaccess/inventory-access-r21/pom.xml
create mode 100644 inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/AnvilInventoryImpl.java
create mode 100644 inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/CartographyInventoryImpl.java
create mode 100644 inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/InventoryUtilsImpl.java
create mode 100644 inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/ItemUtilsImpl.java
create mode 100644 inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/PlayerUtilsImpl.java
diff --git a/README.md b/README.md
index e1322796..13215d11 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
# InvUI
An Inventory API for Minecraft Spigot servers.
-Supports all versions from 1.14.0 to 1.21.
+Supports all versions from 1.14.0 to 1.21.1.
[Documentation](https://xenondevs.xyz/docs/invui/)
diff --git a/inventoryaccess/inventory-access-r21/pom.xml b/inventoryaccess/inventory-access-r21/pom.xml
new file mode 100644
index 00000000..26530171
--- /dev/null
+++ b/inventoryaccess/inventory-access-r21/pom.xml
@@ -0,0 +1,150 @@
+
+
+
+ xyz.xenondevs.invui
+ invui-parent
+ 1.34
+ ../../pom.xml
+
+ 4.0.0
+
+ inventory-access-r21
+
+
+ 21
+ 21
+ 1.21.1
+ ${minecraft.version}-R0.1-SNAPSHOT
+
+
+
+
+ org.spigotmc
+ spigot
+ ${spigot.version}
+ remapped-mojang
+ provided
+
+
+ xyz.xenondevs.invui
+ inventory-access
+ ${project.parent.version}
+
+
+
+
+
+
+ xyz.xenondevs.string-remapper
+ string-remapper-maven-plugin
+ 1.9
+
+
+
+ remap-spigot
+
+ remap
+
+
+ ${minecraft.version}
+ spigot
+ ${project.build.directory}/classes
+ ${project.build.directory}/classes-spigot
+
+
+
+
+ remap-mojang
+
+ remap
+
+
+ ${minecraft.version}
+ mojang
+ ${project.build.directory}/classes
+ ${project.build.directory}/classes-mojang
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.2
+
+
+ default-jar
+ none
+
+
+ spigot
+
+ jar
+
+
+ ${project.build.directory}/classes-spigot
+
+
+ spigot
+
+
+
+
+
+ mojang
+
+ jar
+
+
+ ${project.build.directory}/classes-mojang
+ remapped-mojang
+
+
+ mojang
+
+
+
+
+
+
+
+
+ net.md-5
+ specialsource-maven-plugin
+ 2.0.2
+
+
+ package
+
+ remap
+
+ remap-obf
+
+ org.spigotmc:minecraft-server:${spigot.version}:txt:maps-mojang
+ true
+ org.spigotmc:spigot:${spigot.version}:jar:remapped-mojang
+ false
+ ${project.artifactId}-${project.version}-remapped-obf
+
+
+
+ package
+
+ remap
+
+ remap-spigot
+
+ ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar
+ org.spigotmc:minecraft-server:${spigot.version}:csrg:maps-spigot
+ org.spigotmc:spigot:${spigot.version}:jar:remapped-obf
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/AnvilInventoryImpl.java b/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/AnvilInventoryImpl.java
new file mode 100644
index 00000000..691ba6c1
--- /dev/null
+++ b/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/AnvilInventoryImpl.java
@@ -0,0 +1,188 @@
+package xyz.xenondevs.inventoryaccess.r21;
+
+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.world.Container;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.AnvilMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.inventory.MenuType;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.view.CraftAnvilView;
+import org.bukkit.event.inventory.PrepareAnvilEvent;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.NotNull;
+import xyz.xenondevs.inventoryaccess.abstraction.inventory.AnvilInventory;
+import xyz.xenondevs.inventoryaccess.component.ComponentWrapper;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+class AnvilInventoryImpl extends AnvilMenu implements AnvilInventory {
+
+ private final List> renameHandlers;
+ private final CraftAnvilView view;
+ private final ServerPlayer player;
+
+ private String text;
+ private boolean open;
+
+ public AnvilInventoryImpl(org.bukkit.entity.Player player, @NotNull ComponentWrapper title, List> renameHandlers) {
+ this(((CraftPlayer) player).getHandle(), InventoryUtilsImpl.createNMSComponent(title), renameHandlers);
+ }
+
+ public AnvilInventoryImpl(ServerPlayer player, Component title, List> renameHandlers) {
+ super(player.nextContainerCounter(), player.getInventory(),
+ ContainerLevelAccess.create(player.level(), new BlockPos(0, 0, 0)));
+
+ setTitle(title);
+ this.renameHandlers = renameHandlers;
+ this.player = player;
+
+ CraftInventoryAnvil inventory = new CraftInventoryAnvil(access.getLocation(), inputSlots, resultSlots);
+ this.view = new CraftAnvilView(player.getBukkitEntity(), inventory, this);
+ }
+
+ public void open() {
+ open = true;
+
+ // call the InventoryOpenEvent
+ CraftEventFactory.callInventoryOpenEvent(player, this);
+
+ // set active container
+ player.containerMenu = this;
+
+ // send open packet
+ player.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, getTitle()));
+
+ // send initial items
+ NonNullList itemsList = NonNullList.of(ItemStack.EMPTY, getItem(0), getItem(1), getItem(2));
+ player.connection.send(new ClientboundContainerSetContentPacket(getActiveWindowId(player), incrementStateId(), itemsList, ItemStack.EMPTY));
+
+ // init menu
+ player.initMenu(this);
+ }
+
+ public void sendItem(int slot) {
+ player.connection.send(new ClientboundContainerSetSlotPacket(getActiveWindowId(player), incrementStateId(), slot, getItem(slot)));
+ }
+
+ public void setItem(int slot, ItemStack item) {
+ if (slot < 2) inputSlots.setItem(slot, item);
+ else resultSlots.setItem(0, item);
+
+ if (open) sendItem(slot);
+ }
+
+ private ItemStack getItem(int slot) {
+ if (slot < 2) return inputSlots.getItem(slot);
+ else return resultSlots.getItem(0);
+ }
+
+ private int getActiveWindowId(ServerPlayer player) {
+ AbstractContainerMenu container = player.containerMenu;
+ return container == null ? -1 : container.containerId;
+ }
+
+ @Override
+ public void setItem(int slot, org.bukkit.inventory.ItemStack itemStack) {
+ setItem(slot, CraftItemStack.asNMSCopy(itemStack));
+ }
+
+ @Override
+ public @NotNull Inventory getBukkitInventory() {
+ return view.getTopInventory();
+ }
+
+ @Override
+ public String getRenameText() {
+ return text;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return open;
+ }
+
+ // --- AnvilMenu ---
+
+ @Override
+ public CraftAnvilView getBukkitView() {
+ return view;
+ }
+
+ /**
+ * Called every tick to see if the {@link Player} can still use that container.
+ * (Used to for checking the distance between the {@link Player} and the container
+ * and closing the window when the distance gets too big.)
+ *
+ * @param player The {@link Player}
+ * @return If the {@link Player} can still use that container
+ */
+ @Override
+ public boolean stillValid(Player player) {
+ return true;
+ }
+
+ /**
+ * Called when the rename text gets changed.
+ *
+ * @param s The new rename text
+ */
+ @Override
+ public boolean setItemName(String s) {
+ // save rename text
+ text = s;
+
+ // call rename handlers
+ if (renameHandlers != null)
+ renameHandlers.forEach(handler -> handler.accept(s));
+
+ // the client expects the item to change to its new name and removes it from the inventory, so it needs to be sent again
+ sendItem(2);
+
+ return false;
+ }
+
+ /**
+ * Called when the container is closed to give the items back.
+ *
+ * @param player The {@link Player} that closed this container
+ */
+ @Override
+ public void removed(Player player) {
+ open = false;
+ }
+
+ /**
+ * Called when the container gets closed to put items back into a players
+ * inventory or drop them in the world.
+ *
+ * @param player The {@link Player} that closed this container
+ * @param container The container
+ */
+ @Override
+ protected void clearContainer(Player player, Container container) {
+ open = false;
+ }
+
+ /**
+ * Called when both items in the {@link AnvilMenu#inputSlots} were set to create
+ * the resulting product, calculate the level cost and call the {@link PrepareAnvilEvent}.
+ */
+ @Override
+ public void createResult() {
+ // empty
+ }
+
+}
diff --git a/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/CartographyInventoryImpl.java b/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/CartographyInventoryImpl.java
new file mode 100644
index 00000000..d73dfdc6
--- /dev/null
+++ b/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/CartographyInventoryImpl.java
@@ -0,0 +1,136 @@
+package xyz.xenondevs.inventoryaccess.r21;
+
+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.world.Container;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.*;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryCartography;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventoryView;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
+import org.bukkit.inventory.Inventory;
+import org.jetbrains.annotations.NotNull;
+import xyz.xenondevs.inventoryaccess.abstraction.inventory.CartographyInventory;
+import xyz.xenondevs.inventoryaccess.component.ComponentWrapper;
+import xyz.xenondevs.inventoryaccess.util.ReflectionUtils;
+
+import java.lang.reflect.Field;
+
+class CartographyInventoryImpl extends CartographyTableMenu implements CartographyInventory {
+
+ private static final Field RESULT_CONTAINER_FIELD = ReflectionUtils.getField(
+ CartographyTableMenu.class,
+ true,
+ "SRF(net.minecraft.world.inventory.CartographyTableMenu resultContainer)"
+ );
+
+ private final ResultContainer resultContainer = ReflectionUtils.getFieldValue(RESULT_CONTAINER_FIELD, this);
+ private final CraftInventoryView view;
+ private final ServerPlayer player;
+
+ private boolean open;
+
+ public CartographyInventoryImpl(org.bukkit.entity.Player player, @NotNull ComponentWrapper title) {
+ this(((CraftPlayer) player).getHandle(), InventoryUtilsImpl.createNMSComponent(title));
+ }
+
+ public CartographyInventoryImpl(ServerPlayer player, Component title) {
+ super(player.nextContainerCounter(), player.getInventory(), ContainerLevelAccess.create(player.level(), new BlockPos(0, 0, 0)));
+
+ this.player = player;
+ setTitle(title);
+ CraftInventoryCartography inventory = new CraftInventoryCartography(container, resultContainer);
+ view = new CraftInventoryView(player.getBukkitEntity(), inventory, this);
+ }
+
+ public void open() {
+ open = true;
+
+ // call the InventoryOpenEvent
+ CraftEventFactory.callInventoryOpenEvent(player, this);
+
+ // set active container
+ player.containerMenu = this;
+
+ // send open packet
+ player.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.CARTOGRAPHY_TABLE, getTitle()));
+
+ // send initial items
+ NonNullList itemsList = NonNullList.of(ItemStack.EMPTY, getItem(0), getItem(1), getItem(2));
+ player.connection.send(new ClientboundContainerSetContentPacket(InventoryUtilsImpl.getActiveWindowId(player), incrementStateId(), itemsList, ItemStack.EMPTY));
+
+ // init menu
+ player.initMenu(this);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return open;
+ }
+
+ public void sendItem(int slot) {
+ player.connection.send(new ClientboundContainerSetSlotPacket(InventoryUtilsImpl.getActiveWindowId(player), slot, incrementStateId(), getItem(slot)));
+ }
+
+ public void setItem(int slot, ItemStack item) {
+ if (slot < 2) container.setItem(slot, item);
+ else resultContainer.setItem(0, item);
+
+ if (open) sendItem(slot);
+ }
+
+ private ItemStack getItem(int slot) {
+ if (slot < 2) return container.getItem(slot);
+ else return resultContainer.getItem(0);
+ }
+
+ @Override
+ public void setItem(int slot, org.bukkit.inventory.ItemStack itemStack) {
+ setItem(slot, CraftItemStack.asNMSCopy(itemStack));
+ }
+
+ @Override
+ public Inventory getBukkitInventory() {
+ return view.getTopInventory();
+ }
+
+ // --- CartographyTableMenu ---
+
+ @Override
+ public CraftInventoryView getBukkitView() {
+ return view;
+ }
+
+ @Override
+ public void slotsChanged(Container container) {
+ }
+
+ @Override
+ public ItemStack quickMoveStack(Player player, int i) {
+ return ItemStack.EMPTY;
+ }
+
+ @Override
+ public boolean canTakeItemForPickAll(ItemStack itemstack, Slot slot) {
+ return true;
+ }
+
+ @Override
+ public boolean stillValid(Player player) {
+ return true;
+ }
+
+ @Override
+ protected void clearContainer(Player player, Container container) {
+ // empty
+ }
+
+}
diff --git a/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/InventoryUtilsImpl.java b/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/InventoryUtilsImpl.java
new file mode 100644
index 00000000..4b40f146
--- /dev/null
+++ b/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/InventoryUtilsImpl.java
@@ -0,0 +1,80 @@
+package xyz.xenondevs.inventoryaccess.r21;
+
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.Container;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.MenuType;
+import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftContainer;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory;
+import org.bukkit.craftbukkit.v1_21_R1.util.CraftChatMessage;
+import org.bukkit.entity.Player;
+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;
+import xyz.xenondevs.inventoryaccess.abstraction.util.InventoryUtils;
+import xyz.xenondevs.inventoryaccess.component.ComponentWrapper;
+
+class InventoryUtilsImpl implements InventoryUtils {
+
+ public static Component createNMSComponent(ComponentWrapper component) {
+ if (component == null) return null;
+ return CraftChatMessage.fromJSON(component.serializeToJson());
+ }
+
+ public static int getActiveWindowId(ServerPlayer player) {
+ AbstractContainerMenu container = player.containerMenu;
+ return container == null ? -1 : container.containerId;
+ }
+
+ @Override
+ public void openCustomInventory(@NotNull Player player, @NotNull Inventory inventory) {
+ openCustomInventory(player, inventory, null);
+ }
+
+ @Override
+ public void openCustomInventory(@NotNull Player player, @NotNull Inventory inventory, @Nullable ComponentWrapper title) {
+ ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
+ MenuType> menuType = CraftContainer.getNotchInventoryType(inventory);
+
+ if (serverPlayer.connection != null) {
+ AbstractContainerMenu menu = new CraftContainer(inventory, serverPlayer, serverPlayer.nextContainerCounter());
+ menu = CraftEventFactory.callInventoryOpenEvent(serverPlayer, menu);
+ if (menu != null) {
+ Container container = ((CraftInventory) inventory).getInventory();
+ Component titleComponent;
+ if (title == null) {
+ if (container instanceof MenuProvider)
+ titleComponent = ((MenuProvider) container).getDisplayName();
+ else titleComponent = CraftChatMessage.fromString(menu.getBukkitView().getTitle())[0];
+ } else titleComponent = createNMSComponent(title);
+
+ menu.checkReachable = false;
+ serverPlayer.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menuType, titleComponent));
+ serverPlayer.containerMenu = menu;
+ serverPlayer.initMenu(menu);
+ }
+ }
+
+ }
+
+ @Override
+ public void updateOpenInventoryTitle(@NotNull Player player, @NotNull ComponentWrapper title) {
+ ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
+ AbstractContainerMenu menu = serverPlayer.containerMenu;
+ serverPlayer.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), createNMSComponent(title)));
+ serverPlayer.initMenu(menu);
+ }
+
+ @Override
+ public @Nullable ItemStack getItemStackFromView(@NotNull InventoryView view, int slot) {
+ return view.getItem(slot);
+ }
+
+}
diff --git a/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/ItemUtilsImpl.java b/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/ItemUtilsImpl.java
new file mode 100644
index 00000000..c36ced9e
--- /dev/null
+++ b/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/ItemUtilsImpl.java
@@ -0,0 +1,104 @@
+package xyz.xenondevs.inventoryaccess.r21;
+
+import com.mojang.serialization.Dynamic;
+import net.minecraft.nbt.*;
+import net.minecraft.util.datafix.DataFixers;
+import net.minecraft.util.datafix.fixes.References;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.v1_21_R1.CraftRegistry;
+import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.v1_21_R1.util.CraftMagicNumbers;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.jetbrains.annotations.NotNull;
+import xyz.xenondevs.inventoryaccess.abstraction.util.ItemUtils;
+import xyz.xenondevs.inventoryaccess.component.ComponentWrapper;
+import xyz.xenondevs.inventoryaccess.util.ReflectionRegistry;
+import xyz.xenondevs.inventoryaccess.util.ReflectionUtils;
+
+import java.io.*;
+import java.util.List;
+import java.util.stream.Collectors;
+
+class ItemUtilsImpl implements ItemUtils {
+
+ @Override
+ public byte[] serializeItemStack(org.bukkit.inventory.@NotNull ItemStack itemStack, boolean compressed) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ serializeItemStack(itemStack, out, compressed);
+ return out.toByteArray();
+ }
+
+ @Override
+ public void serializeItemStack(org.bukkit.inventory.@NotNull ItemStack itemStack, @NotNull OutputStream outputStream, boolean compressed) {
+ try {
+ ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
+ CompoundTag nbt = (CompoundTag) nmsStack.save(CraftRegistry.getMinecraftRegistry(), new CompoundTag());
+
+ if (compressed) {
+ NbtIo.writeCompressed(nbt, outputStream);
+ } else {
+ DataOutputStream dataOut = new DataOutputStream(outputStream);
+ NbtIo.write(nbt, dataOut);
+ }
+
+ outputStream.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public org.bukkit.inventory.ItemStack deserializeItemStack(byte[] data, boolean compressed) {
+ ByteArrayInputStream in = new ByteArrayInputStream(data);
+ return deserializeItemStack(in, compressed);
+ }
+
+ @Override
+ public org.bukkit.inventory.ItemStack deserializeItemStack(@NotNull InputStream inputStream, boolean compressed) {
+ try {
+ CompoundTag nbt;
+ if (compressed) {
+ nbt = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
+ } else {
+ DataInputStream dataIn = new DataInputStream(inputStream);
+ nbt = NbtIo.read(dataIn);
+ }
+
+ Dynamic converted = DataFixers.getDataFixer()
+ .update(
+ References.ITEM_STACK,
+ new Dynamic<>(NbtOps.INSTANCE, nbt),
+ 3700, CraftMagicNumbers.INSTANCE.getDataVersion()
+ );
+
+ ItemStack itemStack = ItemStack.parse(
+ CraftRegistry.getMinecraftRegistry(),
+ converted.getValue()
+ ).orElse(ItemStack.EMPTY);
+ return CraftItemStack.asCraftMirror(itemStack);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ @Override
+ public void setDisplayName(@NotNull ItemMeta itemMeta, @NotNull ComponentWrapper name) {
+ ReflectionUtils.setFieldValue(
+ ReflectionRegistry.CB_CRAFT_META_ITEM_DISPLAY_NAME_FIELD,
+ itemMeta,
+ InventoryUtilsImpl.createNMSComponent(name)
+ );
+ }
+
+ @Override
+ public void setLore(@NotNull ItemMeta itemMeta, @NotNull List<@NotNull ComponentWrapper> lore) {
+ ReflectionUtils.setFieldValue(
+ ReflectionRegistry.CB_CRAFT_META_ITEM_LORE_FIELD,
+ itemMeta,
+ lore.stream().map(InventoryUtilsImpl::createNMSComponent).collect(Collectors.toList())
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/PlayerUtilsImpl.java b/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/PlayerUtilsImpl.java
new file mode 100644
index 00000000..96966605
--- /dev/null
+++ b/inventoryaccess/inventory-access-r21/src/main/java/xyz/xenondevs/inventoryaccess/r21/PlayerUtilsImpl.java
@@ -0,0 +1,116 @@
+package xyz.xenondevs.inventoryaccess.r21;
+
+import net.minecraft.core.Holder;
+import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket;
+import net.minecraft.server.PlayerAdvancements;
+import net.minecraft.server.ServerAdvancementManager;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.level.saveddata.maps.*;
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_21_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import xyz.xenondevs.inventoryaccess.abstraction.util.PlayerUtils;
+import xyz.xenondevs.inventoryaccess.map.MapIcon;
+import xyz.xenondevs.inventoryaccess.map.MapPatch;
+import xyz.xenondevs.inventoryaccess.util.ReflectionUtils;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+class PlayerUtilsImpl implements PlayerUtils {
+
+ private static final Method REGISTER_LISTENERS_METHOD = ReflectionUtils.getMethod(
+ PlayerAdvancements.class,
+ true,
+ "ASRM(net/minecraft/server/PlayerAdvancements.registerListeners(Lnet/minecraft/server/ServerAdvancementManager;)V)",
+ ServerAdvancementManager.class
+ );
+
+ @Override
+ public void stopAdvancementListening(@NotNull Player player) {
+ stopAdvancementListening(((CraftPlayer) player).getHandle());
+ }
+
+ @Override
+ public void stopAdvancementListening(@NotNull Object player) {
+ ((ServerPlayer) player).getAdvancements().stopListening();
+ }
+
+ @Override
+ public void startAdvancementListening(@NotNull Player player) {
+ startAdvancementListening(((CraftPlayer) player).getHandle());
+ }
+
+ @Override
+ public void startAdvancementListening(@NotNull Object player) {
+ PlayerAdvancements advancements = ((ServerPlayer) player).getAdvancements();
+ ServerAdvancementManager manager = ((CraftServer) Bukkit.getServer()).getServer().getAdvancements();
+ ReflectionUtils.invokeMethod(REGISTER_LISTENERS_METHOD, advancements, manager);
+ }
+
+ @Override
+ public void sendMapUpdate(@NotNull Player player, int mapId, byte scale, boolean locked, @Nullable MapPatch mapPatch, @Nullable List icons) {
+ List decorations = icons != null ? icons.stream().map(this::toMapDecoration).collect(Collectors.toCollection(ArrayList::new)) : null;
+ MapItemSavedData.MapPatch patch = toMapPatch(mapPatch);
+ ClientboundMapItemDataPacket packet = new ClientboundMapItemDataPacket(new MapId(mapId), scale, locked, decorations, patch);
+ ((CraftPlayer) player).getHandle().connection.send(packet);
+ }
+
+ private MapDecoration toMapDecoration(MapIcon icon) {
+ return new MapDecoration(
+ getDecorationTypeByIconType(icon.getType()),
+ icon.getX(), icon.getY(),
+ icon.getRot(),
+ Optional.ofNullable(icon.getComponent()).map(InventoryUtilsImpl::createNMSComponent)
+ );
+ }
+
+ private MapItemSavedData.MapPatch toMapPatch(MapPatch patch) {
+ if (patch == null) return null;
+
+ return new MapItemSavedData.MapPatch(
+ patch.getStartX(), patch.getStartY(),
+ patch.getWidth(), patch.getHeight(),
+ patch.getColors()
+ );
+ }
+
+ private Holder getDecorationTypeByIconType(MapIcon.MapIconType icon) {
+ return switch(icon) {
+ case WHITE_ARROW -> MapDecorationTypes.PLAYER;
+ case GREEN_ARROW -> MapDecorationTypes.FRAME;
+ case RED_ARROW -> MapDecorationTypes.RED_MARKER;
+ case BLUE_ARROW -> MapDecorationTypes.BLUE_MARKER;
+ case WHITE_CROSS -> MapDecorationTypes.TARGET_X;
+ case RED_POINTER -> MapDecorationTypes.TARGET_POINT;
+ case WHITE_CIRCLE -> MapDecorationTypes.PLAYER_OFF_MAP;
+ case SMALL_WHITE_CIRCLE -> MapDecorationTypes.PLAYER_OFF_LIMITS;
+ case MANSION -> MapDecorationTypes.WOODLAND_MANSION;
+ case TEMPLE -> MapDecorationTypes.JUNGLE_TEMPLE;
+ case WHITE_BANNER -> MapDecorationTypes.WHITE_BANNER;
+ case ORANGE_BANNER -> MapDecorationTypes.ORANGE_BANNER;
+ case MAGENTA_BANNER -> MapDecorationTypes.MAGENTA_BANNER;
+ case LIGHT_BLUE_BANNER -> MapDecorationTypes.LIGHT_BLUE_BANNER;
+ case YELLOW_BANNER -> MapDecorationTypes.YELLOW_BANNER;
+ case LIME_BANNER -> MapDecorationTypes.LIME_BANNER;
+ case PINK_BANNER -> MapDecorationTypes.PINK_BANNER;
+ case GRAY_BANNER -> MapDecorationTypes.GRAY_BANNER;
+ case LIGHT_GRAY_BANNER -> MapDecorationTypes.LIGHT_GRAY_BANNER;
+ case CYAN_BANNER -> MapDecorationTypes.CYAN_BANNER;
+ case PURPLE_BANNER -> MapDecorationTypes.PURPLE_BANNER;
+ case BLUE_BANNER -> MapDecorationTypes.BLUE_BANNER;
+ case BROWN_BANNER -> MapDecorationTypes.BROWN_BANNER;
+ case GREEN_BANNER -> MapDecorationTypes.GREEN_BANNER;
+ case RED_BANNER -> MapDecorationTypes.RED_BANNER;
+ case BLACK_BANNER -> MapDecorationTypes.BLACK_BANNER;
+ case RED_CROSS -> MapDecorationTypes.RED_X;
+ };
+ }
+
+}
diff --git a/inventoryaccess/inventory-access/src/main/java/xyz/xenondevs/inventoryaccess/version/InventoryAccessRevision.java b/inventoryaccess/inventory-access/src/main/java/xyz/xenondevs/inventoryaccess/version/InventoryAccessRevision.java
index 0654cad7..6a0515aa 100644
--- a/inventoryaccess/inventory-access/src/main/java/xyz/xenondevs/inventoryaccess/version/InventoryAccessRevision.java
+++ b/inventoryaccess/inventory-access/src/main/java/xyz/xenondevs/inventoryaccess/version/InventoryAccessRevision.java
@@ -5,6 +5,7 @@
public enum InventoryAccessRevision {
// this order is required
+ R21("r21", "1.21.1"),
R20("r20", "1.21.0"),
R19("r19", "1.20.5"),
R18("r18", "1.20.3"),
diff --git a/invui/pom.xml b/invui/pom.xml
index 5fcec983..0a9dae66 100644
--- a/invui/pom.xml
+++ b/invui/pom.xml
@@ -123,6 +123,11 @@
inventory-access-r20
${project.version}
+
+ xyz.xenondevs.invui
+ inventory-access-r21
+ ${project.version}
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 508c8e65..602db745 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,6 +83,7 @@
inventoryaccess/inventory-access-r18
inventoryaccess/inventory-access-r19
inventoryaccess/inventory-access-r20
+ inventoryaccess/inventory-access-r21
invui-core
invui-kotlin
invui