diff --git a/InvUI/pom.xml b/InvUI/pom.xml index 74d7306..c13185b 100644 --- a/InvUI/pom.xml +++ b/InvUI/pom.xml @@ -78,6 +78,11 @@ 1_17_R1 0.1-SNAPSHOT + + de.studiocode.invui + 1_17_R2 + 0.1-SNAPSHOT + diff --git a/InventoryAccess/1_17_R1/pom.xml b/InventoryAccess/1_17_R1/pom.xml index e5b39a7..97bb9c7 100644 --- a/InventoryAccess/1_17_R1/pom.xml +++ b/InventoryAccess/1_17_R1/pom.xml @@ -15,13 +15,14 @@ 16 16 + 1.17-R0.1-SNAPSHOT org.spigotmc spigot - 1.17-R0.1-SNAPSHOT + ${spigotVersion} remapped-mojang provided @@ -46,9 +47,9 @@ remap-obf - org.spigotmc:minecraft-server:1.17-R0.1-SNAPSHOT:txt:maps-mojang + org.spigotmc:minecraft-server:${spigotVersion}:txt:maps-mojang true - org.spigotmc:spigot:1.17-R0.1-SNAPSHOT:jar:remapped-mojang + org.spigotmc:spigot:${spigotVersion}:jar:remapped-mojang true remapped-obf @@ -61,8 +62,8 @@ remap-spigot ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar - org.spigotmc:minecraft-server:1.17-R0.1-SNAPSHOT:csrg:maps-spigot - org.spigotmc:spigot:1.17-R0.1-SNAPSHOT:jar:remapped-obf + org.spigotmc:minecraft-server:${spigotVersion}:csrg:maps-spigot + org.spigotmc:spigot:${spigotVersion}:jar:remapped-obf diff --git a/InventoryAccess/1_17_R2/pom.xml b/InventoryAccess/1_17_R2/pom.xml new file mode 100644 index 0000000..ab24c4f --- /dev/null +++ b/InventoryAccess/1_17_R2/pom.xml @@ -0,0 +1,74 @@ + + + + de.studiocode.invui + InvUI-Parent + 0.1-SNAPSHOT + ../../pom.xml + + 4.0.0 + + 1_17_R2 + + + 16 + 16 + 1.17.1-R0.1-SNAPSHOT + + + + + org.spigotmc + spigot + ${spigotVersion} + remapped-mojang + provided + + + de.studiocode.invui + api + ${project.parent.version} + + + + + + + net.md-5 + specialsource-maven-plugin + 1.2.2 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:${spigotVersion}:txt:maps-mojang + true + org.spigotmc:spigot:${spigotVersion}:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:${spigotVersion}:csrg:maps-spigot + org.spigotmc:spigot:${spigotVersion}:jar:remapped-obf + + + + + + + + \ No newline at end of file diff --git a/InventoryAccess/1_17_R2/src/main/java/de/studiocode/inventoryaccess/v1_17_R2/inventory/AnvilInventoryImpl.java b/InventoryAccess/1_17_R2/src/main/java/de/studiocode/inventoryaccess/v1_17_R2/inventory/AnvilInventoryImpl.java new file mode 100644 index 0000000..410f55f --- /dev/null +++ b/InventoryAccess/1_17_R2/src/main/java/de/studiocode/inventoryaccess/v1_17_R2/inventory/AnvilInventoryImpl.java @@ -0,0 +1,188 @@ +package de.studiocode.inventoryaccess.v1_17_R2.inventory; + +import de.studiocode.inventoryaccess.api.abstraction.inventory.AnvilInventory; +import de.studiocode.inventoryaccess.v1_17_R2.util.InventoryUtilsImpl; +import net.md_5.bungee.api.chat.BaseComponent; +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_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryAnvil; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.event.inventory.PrepareAnvilEvent; +import org.bukkit.inventory.Inventory; + +import java.util.function.Consumer; + +public class AnvilInventoryImpl extends AnvilMenu implements AnvilInventory { + + private final Component title; + private final Consumer renameHandler; + private final CraftInventoryView view; + private final ServerPlayer player; + + private String text; + private boolean open; + + public AnvilInventoryImpl(org.bukkit.entity.Player player, BaseComponent[] title, Consumer renameHandler) { + this(((CraftPlayer) player).getHandle(), InventoryUtilsImpl.createNMSComponent(title), renameHandler); + } + + public AnvilInventoryImpl(ServerPlayer player, Component title, Consumer renameHandler) { + super(player.nextContainerCounter(), player.getInventory(), + ContainerLevelAccess.create(player.level, new BlockPos(Integer.MAX_VALUE, 0, 0))); + + this.title = title; + this.renameHandler = renameHandler; + this.player = player; + + CraftInventoryAnvil inventory = new CraftInventoryAnvil(access.getLocation(), + inputSlots, resultSlots, this); + this.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.ANVIL, title)); + + // 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), slot, incrementStateId(), 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 Inventory getBukkitInventory() { + return view.getTopInventory(); + } + + @Override + public String getRenameText() { + return text; + } + + @Override + public boolean isOpen() { + return open; + } + + // --- ContainerAnvil --- + + @Override + public CraftInventoryView 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 void setItemName(String s) { + // save rename text + text = s; + + // call the rename handler + if (renameHandler != null) renameHandler.accept(s); + + // the client expects the item to change to it's new name and removes it from the inventory, so it needs to be sent again + sendItem(2); + } + + /** + * 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/1_17_R2/src/main/java/de/studiocode/inventoryaccess/v1_17_R2/util/InventoryUtilsImpl.java b/InventoryAccess/1_17_R2/src/main/java/de/studiocode/inventoryaccess/v1_17_R2/util/InventoryUtilsImpl.java new file mode 100644 index 0000000..855aadf --- /dev/null +++ b/InventoryAccess/1_17_R2/src/main/java/de/studiocode/inventoryaccess/v1_17_R2/util/InventoryUtilsImpl.java @@ -0,0 +1,66 @@ +package de.studiocode.inventoryaccess.v1_17_R2.util; + +import de.studiocode.inventoryaccess.api.abstraction.util.InventoryUtils; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.chat.ComponentSerializer; +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_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.event.CraftEventFactory; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftContainer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftInventory; +import org.bukkit.craftbukkit.v1_17_R1.util.CraftChatMessage; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; + +public class InventoryUtilsImpl implements InventoryUtils { + + @Override + public void openCustomInventory(Player player, Inventory inventory) { + openCustomInventory(player, inventory, null); + } + + @Override + public void openCustomInventory(Player player, Inventory inventory, BaseComponent[] 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(Player player, BaseComponent[] title) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + AbstractContainerMenu menu = serverPlayer.containerMenu; + serverPlayer.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), createNMSComponent(title))); + } + + public static Component createNMSComponent(BaseComponent[] components) { + String json = ComponentSerializer.toString(components); + return CraftChatMessage.fromJSON(json); + } + +} diff --git a/InventoryAccess/1_17_R2/src/main/java/de/studiocode/inventoryaccess/v1_17_R2/util/ItemUtilsImpl.java b/InventoryAccess/1_17_R2/src/main/java/de/studiocode/inventoryaccess/v1_17_R2/util/ItemUtilsImpl.java new file mode 100644 index 0000000..eba8316 --- /dev/null +++ b/InventoryAccess/1_17_R2/src/main/java/de/studiocode/inventoryaccess/v1_17_R2/util/ItemUtilsImpl.java @@ -0,0 +1,75 @@ +package de.studiocode.inventoryaccess.v1_17_R2.util; + +import de.studiocode.inventoryaccess.api.abstraction.util.ItemUtils; +import de.studiocode.inventoryaccess.api.version.ReflectionUtils; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; + +import java.io.*; +import java.lang.reflect.Field; + +public class ItemUtilsImpl implements ItemUtils { + + private static final Field CRAFT_ITEM_STACK_HANDLE_FIELD = ReflectionUtils.getField(CraftItemStack.class, true, "handle"); + + @Override + public byte[] serializeItemStack(org.bukkit.inventory.ItemStack itemStack, boolean compressed) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serializeItemStack(itemStack, out, compressed); + return out.toByteArray(); + } + + @Override + public void serializeItemStack(org.bukkit.inventory.ItemStack itemStack, OutputStream outputStream, boolean compressed) { + try { + ItemStack nmsStack; + + if (itemStack instanceof CraftItemStack) + nmsStack = (ItemStack) CRAFT_ITEM_STACK_HANDLE_FIELD.get(itemStack); + else nmsStack = CraftItemStack.asNMSCopy(itemStack); + + CompoundTag nbt = nmsStack.save(new CompoundTag()); + + if (compressed) { + NbtIo.writeCompressed(nbt, outputStream); + } else { + DataOutputStream dataOut = new DataOutputStream(outputStream); + NbtIo.write(nbt, dataOut); + } + + outputStream.flush(); + } catch (IllegalAccessException | 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(InputStream inputStream, boolean compressed) { + try { + CompoundTag nbt; + if (compressed) { + nbt = NbtIo.readCompressed(inputStream); + } else { + DataInputStream dataIn = new DataInputStream(inputStream); + nbt = NbtIo.read(dataIn); + } + + ItemStack itemStack = ItemStack.of(nbt); + + return CraftItemStack.asCraftMirror(itemStack); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + +} \ No newline at end of file diff --git a/InventoryAccess/api/src/main/java/de/studiocode/inventoryaccess/api/version/ReflectionUtils.java b/InventoryAccess/api/src/main/java/de/studiocode/inventoryaccess/api/version/ReflectionUtils.java index b710507..7d57c63 100644 --- a/InventoryAccess/api/src/main/java/de/studiocode/inventoryaccess/api/version/ReflectionUtils.java +++ b/InventoryAccess/api/src/main/java/de/studiocode/inventoryaccess/api/version/ReflectionUtils.java @@ -13,8 +13,15 @@ public class ReflectionUtils { private static final String VERSION = getVersion(); private static String getVersion() { - String path = Bukkit.getServer().getClass().getPackage().getName(); - return path.substring(path.lastIndexOf(".") + 1); + String version = Bukkit.getVersion(); + version = version.substring(version.indexOf("MC: "), version.length() - 1).substring(4); + + if (version.equals("1.17.1")) { + return "v1_17_R2"; // TODO: find a better solution + } else { + String path = Bukkit.getServer().getClass().getPackage().getName(); + return path.substring(path.lastIndexOf(".") + 1); + } } public static Class getImplClass(String path) { diff --git a/pom.xml b/pom.xml index 04aa08e..a395705 100644 --- a/pom.xml +++ b/pom.xml @@ -31,6 +31,7 @@ InventoryAccess/1_16_R2 InventoryAccess/1_16_R3 InventoryAccess/1_17_R1 + InventoryAccess/1_17_R2 InventoryAccess/api InvUI