+ * Copyright (C) Kristian S. Strangeland
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.comphenix.packetwrapper;
+
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.wrappers.BlockPosition;
+
+public class WrapperPlayClientUpdateSign extends AbstractPacket {
+ public static final PacketType TYPE = PacketType.Play.Client.UPDATE_SIGN;
+
+ public WrapperPlayClientUpdateSign() {
+ super(new PacketContainer(TYPE), TYPE);
+ handle.getModifier().writeDefaults();
+ }
+
+ public WrapperPlayClientUpdateSign(PacketContainer packet) {
+ super(packet, TYPE);
+ }
+
+ /**
+ * Retrieve Location.
+ *
+ * Notes: block Coordinates
+ *
+ * @return The current Location
+ */
+ public BlockPosition getLocation() {
+ return handle.getBlockPositionModifier().read(0);
+ }
+
+ /**
+ * Set Location.
+ *
+ * @param value - new value.
+ */
+ public void setLocation(BlockPosition value) {
+ handle.getBlockPositionModifier().write(0, value);
+ }
+
+ /**
+ * Retrieve this sign's lines of text.
+ *
+ * @return The current lines
+ */
+ public String[] getLines() {
+ return handle.getStringArrays().read(0);
+ }
+
+ /**
+ * Set this sign's lines of text.
+ *
+ * @param value - Lines, must be 4 elements long
+ */
+ public void setLines(String[] value) {
+ if (value == null)
+ throw new IllegalArgumentException("value cannot be null!");
+ if (value.length != 4)
+ throw new IllegalArgumentException("value must have 4 elements!");
+
+ handle.getStringArrays().write(0, value);
+ }
+}
\ No newline at end of file
diff --git a/Itemutils/src/main/java/com/comphenix/packetwrapper/WrapperPlayServerBlockChange.java b/Itemutils/src/main/java/com/comphenix/packetwrapper/WrapperPlayServerBlockChange.java
new file mode 100644
index 0000000..5b72073
--- /dev/null
+++ b/Itemutils/src/main/java/com/comphenix/packetwrapper/WrapperPlayServerBlockChange.java
@@ -0,0 +1,88 @@
+/**
+ * PacketWrapper - ProtocolLib wrappers for Minecraft packets
+ * Copyright (C) dmulloy2
+ * Copyright (C) Kristian S. Strangeland
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.comphenix.packetwrapper;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.wrappers.BlockPosition;
+import com.comphenix.protocol.wrappers.WrappedBlockData;
+
+public class WrapperPlayServerBlockChange extends AbstractPacket {
+ public static final PacketType TYPE = PacketType.Play.Server.BLOCK_CHANGE;
+
+ public WrapperPlayServerBlockChange() {
+ super(new PacketContainer(TYPE), TYPE);
+ handle.getModifier().writeDefaults();
+ }
+
+ public WrapperPlayServerBlockChange(PacketContainer packet) {
+ super(packet, TYPE);
+ }
+
+ /**
+ * Retrieve Location.
+ *
+ * Notes: block Coordinates
+ *
+ * @return The current Location
+ */
+ public BlockPosition getLocation() {
+ return handle.getBlockPositionModifier().read(0);
+ }
+
+ /**
+ * Set Location.
+ *
+ * @param value - new value.
+ */
+ public void setLocation(BlockPosition value) {
+ handle.getBlockPositionModifier().write(0, value);
+ }
+
+ /**
+ * Retrieve the Bukkit Location.
+ *
+ * @param world World for the location
+ * @return Bukkit Location
+ */
+ public Location getBukkitLocation(World world) {
+ return getLocation().toVector().toLocation(world);
+ }
+
+ /**
+ * Retrieve Block Data.
+ *
+ * @return The current Block Data
+ */
+ public WrappedBlockData getBlockData() {
+ return handle.getBlockData().read(0);
+ }
+
+ /**
+ * Set Block Data.
+ *
+ * @param value - new value.
+ */
+ public void setBlockData(WrappedBlockData value) {
+ handle.getBlockData().write(0, value);
+ }
+}
\ No newline at end of file
diff --git a/Itemutils/src/main/java/com/comphenix/packetwrapper/WrapperPlayServerOpenSignEditor.java b/Itemutils/src/main/java/com/comphenix/packetwrapper/WrapperPlayServerOpenSignEditor.java
new file mode 100644
index 0000000..7a61266
--- /dev/null
+++ b/Itemutils/src/main/java/com/comphenix/packetwrapper/WrapperPlayServerOpenSignEditor.java
@@ -0,0 +1,57 @@
+/**
+ * PacketWrapper - ProtocolLib wrappers for Minecraft packets
+ * Copyright (C) dmulloy2
+ * Copyright (C) Kristian S. Strangeland
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.comphenix.packetwrapper;
+
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.wrappers.BlockPosition;
+
+public class WrapperPlayServerOpenSignEditor extends AbstractPacket {
+
+ public static final PacketType TYPE =
+ PacketType.Play.Server.OPEN_SIGN_EDITOR;
+
+ public WrapperPlayServerOpenSignEditor() {
+ super(new PacketContainer(TYPE), TYPE);
+ handle.getModifier().writeDefaults();
+ }
+
+ public WrapperPlayServerOpenSignEditor(PacketContainer packet) {
+ super(packet, TYPE);
+ }
+
+ /**
+ * Retrieve Location.
+ *
+ * @return The current Location
+ */
+ public BlockPosition getLocation() {
+ return handle.getBlockPositionModifier().read(0);
+ }
+
+ /**
+ * Set Location.
+ *
+ * @param value - new value.
+ */
+ public void setLocation(BlockPosition value) {
+ handle.getBlockPositionModifier().write(0, value);
+ }
+
+}
diff --git a/Itemutils/src/main/java/eu/mikroskeem/utils/itemutils/FakeSignFactory.java b/Itemutils/src/main/java/eu/mikroskeem/utils/itemutils/FakeSignFactory.java
new file mode 100644
index 0000000..a411b4f
--- /dev/null
+++ b/Itemutils/src/main/java/eu/mikroskeem/utils/itemutils/FakeSignFactory.java
@@ -0,0 +1,177 @@
+package eu.mikroskeem.utils.itemutils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.comphenix.packetwrapper.WrapperPlayClientUpdateSign;
+import com.comphenix.packetwrapper.WrapperPlayServerOpenSignEditor;
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.ProtocolLibrary;
+import com.comphenix.protocol.ProtocolManager;
+import com.comphenix.protocol.events.ListenerPriority;
+import com.comphenix.protocol.events.PacketAdapter;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.events.PacketEvent;
+import com.comphenix.protocol.wrappers.BlockPosition;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.util.Vector;
+
+/**
+ * @author Mark V.
+ */
+public class FakeSignFactory {
+ private ProtocolManager protocolManager;
+ private PacketAdapter packetListener;
+ private Map listeners;
+ private Map signLocations;
+
+ /**
+ * Construct FakeSignFactory
+ *
+ * @param plugin Plugin to register packet listeners on
+ */
+ public FakeSignFactory(Plugin plugin) {
+ protocolManager = ProtocolLibrary.getProtocolManager();
+ packetListener = new PacketListener(plugin);
+ protocolManager.addPacketListener(packetListener);
+ listeners = new ConcurrentHashMap<>();
+ signLocations = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * Open fake sign on player screen
+ *
+ * @param player Player to open sign on
+ * @param response Sign response callback
+ */
+ public void open(Player player, FakeSignListener response) {
+ open(player, (Location)null, response);
+ }
+
+ /**
+ * Open fake sign on player screen
+ *
+ * @param player Player to open sign on
+ * @param signLocation Fake sign location
+ * @param response Sign response callback
+ */
+ public void open(Player player, Location signLocation, FakeSignListener response) {
+ int x = 0, y = 0, z = 0;
+ if (signLocation != null) {
+ x = signLocation.getBlockX();
+ y = signLocation.getBlockY();
+ z = signLocation.getBlockZ();
+ }
+
+ WrapperPlayServerOpenSignEditor openSignEditor = new WrapperPlayServerOpenSignEditor();
+ openSignEditor.setLocation(new BlockPosition(x, y, z));
+
+ try {
+ protocolManager.sendServerPacket(player, openSignEditor.getHandle());
+ signLocations.put(player.getName(), new Vector(x, y, z));
+ listeners.put(player.getName(), response);
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Open fake sign on player screen
+ *
+ * @param player Player to open sign on
+ * @param defaultText Sign default text
+ * @param response Sign response callback
+ */
+ public void open(Player player, String[] defaultText, FakeSignListener response) {
+ List packets = new ArrayList<>();
+
+ int x = 0, y = 255, z = 0;
+ if (defaultText != null) {
+ x = player.getLocation().getBlockX();
+ z = player.getLocation().getBlockZ();
+ Location signLocation = new Location(player.getWorld(), (double)x, (double)y, (double)z);
+
+ /* Send block change */
+ player.sendBlockChange(signLocation, Material.WALL_SIGN, (byte)0);
+
+ /* Set sign content */
+ player.sendSignChange(signLocation, defaultText);
+ }
+
+ /* Open sign editor */
+ WrapperPlayServerOpenSignEditor openSignEditor = new WrapperPlayServerOpenSignEditor();
+ openSignEditor.setLocation(new BlockPosition(x, y, z));
+ packets.add(openSignEditor.getHandle());
+
+ try {
+ for (PacketContainer packet : packets) {
+ protocolManager.sendServerPacket(player, packet);
+ }
+ if (defaultText != null) {
+ /* Make sign disappear */
+ Location signLocation = new Location(player.getWorld(), (double)x, (double)y, (double)z);
+ player.sendBlockChange(signLocation, Material.AIR, (byte)0);
+ }
+ signLocations.put(player.getName(), new Vector(x, y, z));
+ listeners.put(player.getName(), response);
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Destroy FakeSignFactory
+ */
+ public void destroy() {
+ protocolManager.removePacketListener(packetListener);
+ listeners.clear();
+ signLocations.clear();
+ }
+
+ interface FakeSignListener {
+ void onSignDone(Player player, String[] lines);
+ }
+
+ private class PacketListener extends PacketAdapter {
+ Plugin plugin;
+
+ public PacketListener(Plugin plugin) {
+ super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.UPDATE_SIGN);
+ this.plugin = plugin;
+ }
+
+ @Override
+ public void onPacketReceiving(PacketEvent event) {
+ final Player player = event.getPlayer();
+
+ /* Check if it's sign update packet */
+ PacketContainer packet = event.getPacket();
+ if(packet.getType() == PacketType.Play.Client.UPDATE_SIGN){
+ /* Wrap packet */
+ final WrapperPlayClientUpdateSign updateSign = new WrapperPlayClientUpdateSign(packet);
+ Location signLocation = updateSign.getLocation().toLocation(player.getWorld());
+
+ /* Remove sign location from signLocations */
+ Vector v = signLocations.remove(player.getName());
+ if (v == null) return;
+ if (signLocation.getBlockX() != v.getBlockX() ||
+ signLocation.getBlockY() != v.getBlockY() ||
+ signLocation.getBlockZ() != v.getBlockZ()) return;
+
+ /* Do callback */
+ final FakeSignListener response = listeners.remove(event.getPlayer().getName());
+ if (response != null) {
+ event.setCancelled(true);
+ plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, ()->
+ response.onSignDone(player, updateSign.getLines()));
+ }
+ }
+ }
+ }
+}