Solved Sign editing packet not sticking

Discussion in 'Spigot Plugin Development' started by FiXed, Jun 13, 2016.

  1. So I'm using a sign editor packet to open up a sort of sign GUI and I'm having the issue of after I edit the sign and press done the text I inputted doesn't stay on the sign, how would I go about letting the editing stick and not revert back to the original text?
    Here's what my issue looks like: http://i.imgur.com/Bl3MevS.gifv

    My code:
    Code (Java):
        @EventHandler
        public void onRightClick(PlayerInteractEvent event) {
            if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getState() instanceof Sign) {
                Player player = event.getPlayer();
                double x, y, z;
                x = event.getClickedBlock().getX();
                y = event.getClickedBlock().getY();
                z = event.getClickedBlock().getZ();
                PacketPlayOutOpenSignEditor packet = new PacketPlayOutOpenSignEditor(
                        BlockPosition.PooledBlockPosition.d(x, y, z));
                ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
            }
        }
    EDIT (after solve)

    Made a little Sign Editor API for anyone who wants it, pretty easy to use just SignEditor.register(this) in your onEnable and SignEditor.openEditor(Player, Sign, SignResponse) SignResponse is just an inner class to check when the player is done editing their sign, anyway here's the class:
    Code (Java):
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;

    import org.bukkit.Bukkit;
    import org.bukkit.block.Sign;
    import org.bukkit.entity.Player;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.Listener;
    import org.bukkit.event.block.SignChangeEvent;
    import org.bukkit.event.player.PlayerQuitEvent;
    import org.bukkit.plugin.java.JavaPlugin;

    public class SignEditor implements Listener {

        private static SignEditor instance;
        private final String version;
        private final Map<UUID, SignResponse> inEdit = new HashMap<UUID, SignResponse>();

        private SignEditor(JavaPlugin plugin) {
            SignEditor.instance = this;
            this.version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
            plugin.getServer().getPluginManager().registerEvents(this, plugin);
        }

        @EventHandler
        public void onDisconnect(PlayerQuitEvent event) {
            UUID id = event.getPlayer().getUniqueId();
            if (inEdit.containsKey(id))
                inEdit.remove(id);
        }

        @EventHandler
        public void onSignChange(SignChangeEvent event) {
            UUID id = event.getPlayer().getUniqueId();
            if (inEdit.containsKey(id)) {
                inEdit.get(id).onSignFinish(event);
                inEdit.remove(id);
            }
        }

        private final void open(Player player, Sign sign, SignResponse response) {
            for (int i = 0; i < 4; ++i)
                sign.setLine(i, sign.getLine(i).replace("ยง", "&"));
            sign.update();

            try {
                Object handle = player.getClass().getMethod("getHandle").invoke(player);
                Object connection = handle.getClass().getField("playerConnection").get(handle);

                Field tileField = sign.getClass().getDeclaredField("sign");
                tileField.setAccessible(true);
                Object tileSign = tileField.get(sign);

                Field editable = tileSign.getClass().getDeclaredField("isEditable");
                editable.setAccessible(true);
                editable.set(tileSign, true);

                Field handler = tileSign.getClass().getDeclaredField("h");
                handler.setAccessible(true);
                handler.set(tileSign, handle);

                Object position = getNMSClass("BlockPosition$PooledBlockPosition")
                        .getMethod("d", double.class, double.class, double.class)
                        .invoke(null, sign.getX(), sign.getY(), sign.getZ());

                Object packet = getNMSClass("PacketPlayOutOpenSignEditor").getConstructor(getNMSClass("BlockPosition"))
                        .newInstance(position);

                connection.getClass().getDeclaredMethod("sendPacket", getNMSClass("Packet")).invoke(connection, packet);
                inEdit.put(player.getUniqueId(), response);
            } catch (Exception x) {
                x.printStackTrace();
            }
        }

        public Class<?> getNMSClass(String clazz) {
            try {
                return Class.forName("net.minecraft.server." + version + "." + clazz);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        }

        public static final void openEditor(Player player, Sign sign, SignResponse response) {
            instance.open(player, sign, response);
        }

        public static final void register(JavaPlugin plugin) {
            new SignEditor(plugin);
        }
     
        public interface SignResponse {

            void onSignFinish(SignChangeEvent event);

        }
     
    }
     
    #1 FiXed, Jun 13, 2016
    Last edited: Jun 17, 2016
  2. You might want to look at the source code of something like this.
     
  3. use the Sign sign = event.getClickedBlock().getState();
     
  4. why?
    I'll look into it thanks
     
  5. sign.getLine(1, "ClickMe");

    if clicked click me do something.
     
  6. Setup a state statement? On place the sign put add a Metadata, canEdit to true and when you have edit your sign turn it to false.
     
  7. [​IMG]
    Don't really see that option, is there a way to find the tile entity sign that I'm right clicking?
     
  8. I don't really understand how that fixes or helps what I need... 0.o
     
  9. Code (Java):
    public void onRightClick(PlayerInteractEvent event){
           if(event.getAction()==[URL='http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+action']Action[/URL].RIGHT_CLICK_BLOCK&& event.getClickedBlock().getState()instanceof Sign){
       Sign sign = (Sign) e.getClickedBlock().getState();

      if(sign.getLine(0).equals("Hi")) {
          sign.setLine(1, "Click this");
          e.getPlayer().sendMessage("Hello you clicked Hi in the first line of sign");
          return;
      }
    }
     
  10. that's not what I'm trying to do
     
  11. I'm not exactly telling you to use this method I just said use the Sign class

    you've said earlier this happens after you clicked done after editting well then there's a event called SignChangeEvent which you can use.
     
  12. except I don't think it would call sign change event because it doesn't change the sign
     
  13. I think the reason to this not working is because you're only sending the user a packet that opens the editor UI,
    You'll need a way to intercept/ read the text inserted client side and then change the sign to values you've received from the client
     
  14. I actually think it's in the "TileEntitySign.isEditable" field, I think if that were true than I could fix it up but I don't know how to get the TileEntity from the sign to then use reflection to change it.
     
  15. JamesJ

    Supporter

    You have to actually update the sign. This just opens a GUI to change the sign, you're not checking for what's done after you open the GUI.
     
  16. have you based in the JavaDoc about the SignChangeEvent ?
     
  17. how do I get the after bit?
     
  18. I'll look into it