Resource SignMenu [1.16.5] - Get Player Sign Input!

Discussion in 'Spigot Plugin Development' started by FrostedSnowman, Jun 19, 2017.

  1. https://minecraft.gamepedia.com/Java_Edition_20w30a

    Fixed!

    Signs
    • The sign editing screen is now initialized with the existing sign text when the sign is re-edited using third-party tools.
     
    • Like Like x 2
    • Informative Informative x 1
  2. useful but predefined text doesnt work on 1.16.1 paper
     
  3. Is a client side thing for 1.16.1, it was fixed in 1.16.2.

    If you enter lets say with 1.16.2 on a 1.16.1 server the signs will work
     
  4. yes, its working. thanks
     
  5. i need 1.12.2 code can you give past code?
     
  6. FrostedSnowman

    Resource Staff

    Updated to 1.16.5 (still worked before this).

    Changelog (v5.0)
    • Added ability to close the menu (reopen or choose not to reopen)
    • You no longer have to specify all 4 lines for the sign text
    • Removed stream usage, moved back to simple loops
    • Block data (where sign is placed) is now restored to its original state instead of blindly setting air
    • Minor code cleanup
     
    • Useful Useful x 2
  7. Were you trying to do something like this?
    Code (Java):
     public static void foo(Player target) {
            ASLauncher.getInstance().sign.newMenu(Arrays.asList("", "^^^^^^", "Enter your", "search query!")).reopenIfFail(false).response((player, lines) -> {
                Player player1 = Bukkit.getPlayer(lines[0]);
                if (player1 == null) {
                    player.sendMessage(addColors("&cYou didn't enter a search query!"));
                } else if (lines[0].equalsIgnoreCase(player1.getName())) {
                    player.sendMessage(addColors("&aHello " + player1.getName() + ""));
                    return true;
                }
                return false;
            }).open(target.getPlayer());
        }
     
  8. Thank you, its helped me!
     
  9. If anyone wondering, you have to replace this by that: "signMenuFactory" aka the instance of the Class SignMenuFactory.
    :)
     
  10. I have managed to adapt it to these versions, it is simply a slight change in the code and add a method and change parameters of others.

    This is the code:
    Code (Java):
    package ur.package;

    import java.lang.reflect.InvocationTargetException;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.function.BiPredicate;

    import org.bukkit.Bukkit;
    import org.bukkit.ChatColor;
    import org.bukkit.Location;
    import org.bukkit.Material;
    import org.bukkit.entity.Player;
    import org.bukkit.plugin.Plugin;

    import com.comphenix.protocol.PacketType;
    import com.comphenix.protocol.ProtocolLibrary;
    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 com.comphenix.protocol.wrappers.nbt.NbtCompound;

    // This code was made by FrostedSnowman and edited by Atog_.

    @SuppressWarnings("deprecation")
    public final class SignMenuFactory {

        private static final int ACTION_INDEX = 9;
        private static final int SIGN_LINES = 4;

        private static final String NBT_FORMAT = "{\"text\":\"%s\"}";
        private static final String NBT_BLOCK_ID = "minecraft:sign";

        private final Plugin plugin;

        private final Map<Player, Menu> inputs;

        public SignMenuFactory(Plugin plugin) {
            this.plugin = plugin;
            this.inputs = new HashMap<>();
            this.listen();
        }

        public Menu newMenu(List<String> text) {
            return new Menu(text);
        }

        private void listen() {
            ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(this.plugin, PacketType.Play.Client.UPDATE_SIGN) {
             
                @Override
                public void onPacketReceiving(PacketEvent event) {
                    Player player = event.getPlayer();

                    Menu menu = inputs.remove(player);

                    if (menu == null) {
                        return;
                    }
                    event.setCancelled(true);

                    boolean success = menu.response.test(player, event.getPacket().getStringArrays().read(0));

                    if (!success && menu.reopenIfFail && !menu.forceClose) {
                        Bukkit.getScheduler().runTaskLater(plugin, () -> menu.open(player), 2L);
                    }
                    Bukkit.getScheduler().runTaskLater(plugin, () -> {
                        if (player.isOnline()) {
                            Location location = menu.position.toLocation(player.getWorld());
                            player.sendBlockChange(location, Material.WALL_SIGN, (byte)0);
                            player.sendSignChange(location, menu.text.stream().toArray(String[]::new));
                        }
                    }, 2L);
                }
            });
        }

        public final class Menu {

            private final List<String> text;

            private BiPredicate<Player, String[]> response;
            private boolean reopenIfFail;

            private BlockPosition position;

            private boolean forceClose;

            Menu(List<String> text) {
                this.text = text;
            }

            public Menu reopenIfFail(boolean value) {
                this.reopenIfFail = value;
                return this;
            }

            public Menu response(BiPredicate<Player, String[]> response) {
                this.response = response;
                return this;
            }

            public void open(Player player) {
                Objects.requireNonNull(player, "player is null");
                if (!player.isOnline()) {
                    return;
                }
                Location location = player.getLocation();
                this.position = new BlockPosition(location.getBlockX(), location.getBlockY() + (255 - location.getBlockY()), location.getBlockZ());
                player.sendBlockChange(this.position.toLocation(location.getWorld()), Material.WALL_SIGN, (byte)0);
                player.sendSignChange(this.position.toLocation(location.getWorld()), this.text.stream().toArray(String[]::new));
             
                PacketContainer openSign = ProtocolLibrary.getProtocolManager().createPacket(PacketType.Play.Server.OPEN_SIGN_EDITOR);
                PacketContainer signData = ProtocolLibrary.getProtocolManager().createPacket(PacketType.Play.Server.TILE_ENTITY_DATA);

                openSign.getBlockPositionModifier().write(0, this.position);

                NbtCompound signNBT = (NbtCompound) signData.getNbtModifier().read(0);

                for (int line = 0; line < SIGN_LINES; line++) {
                    signNBT.put("Text" + (line + 1), this.text.size() > line ? String.format(NBT_FORMAT, color(this.text.get(line))) : "");
                }

                signNBT.put("x", this.position.getX());
                signNBT.put("y", this.position.getY());
                signNBT.put("z", this.position.getZ());
                signNBT.put("id", NBT_BLOCK_ID);

                signData.getBlockPositionModifier().write(0, this.position);
                signData.getIntegers().write(0, ACTION_INDEX);
                signData.getNbtModifier().write(0, signNBT);

                try {
                    ProtocolLibrary.getProtocolManager().sendServerPacket(player, signData);
                    ProtocolLibrary.getProtocolManager().sendServerPacket(player, openSign);
                } catch (InvocationTargetException exception) {
                    exception.printStackTrace();
                }
                inputs.put(player, this);
            }

            /**
             * closes the menu. if force is true, the menu will close and will ignore the reopen
             * functionality. false by default.
             *
             * @param player the player
             * @param force decides whether or not it will reopen if reopen is enabled
             */

            public void close(Player player, boolean force) {
                this.forceClose = force;
                if (player.isOnline()) {
                    player.closeInventory();
                }
            }

            public void close(Player player) {
                close(player, false);
            }

            private String color(String input) {
                return ChatColor.translateAlternateColorCodes('&', input);
            }
        }
    }
    I hope it will be helpful :D

    EDIT: I had forgotten to fix the problem about the text restarting when reopening the sign menu.
     
    #90 Atog_, Jul 14, 2021
    Last edited: Jul 16, 2021
  11. Ty <3
     
    • Like Like x 1
  12. Can this be updated to 1.17?