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