currentLine = new Vector<>();
- int totalLineLength = 0;
-
- for (; ; current++) {
- MessagePart part = current < parts.size() ? parts.get(current) : null;
- String raw = part == null ? null : ChatColor.stripColor(part.toLegacy());
-
- if (current >= parts.size() || totalLineLength + raw.length() >= 53) {
- int padding = Math.max(0, (53 - totalLineLength) / 2);
- currentLine.firstElement().setText(Strings.repeat(" ", padding) + currentLine.firstElement().getText());
- currentLine.lastElement().setText(currentLine.lastElement().getText() + "\n");
- currentLine.clear();
- break;
- }
-
- totalLineLength += raw.length();
- currentLine.add(part);
- }
- }
-
- MessagePart last = parts.get(parts.size() - 1);
- last.setText(last.getText().substring(0, last.getText().length() - 1));
-
- centeringStartIndex = -1;
-
- return this;
- }
-
- ///////////////////////////
- // BEGIN UTILITY CLASSES //
- ///////////////////////////
-
- /**
- * Represents the JSON format that all click/hover events in JSON messages must follow.
- *
- *
- * Reference
- *
- * @author Rayzr
- */
- public static class MessageEvent {
-
- private String action;
- private Object value;
-
- public MessageEvent(String action, Object value) {
- this.action = action;
- this.value = value;
- }
-
- /**
- * @return A {@link JsonObject} representing the properties of this {@link MessageEvent}
- */
- public JsonObject toJSON() {
- JsonObject obj = new JsonObject();
- obj.addProperty("action", action);
- if (value instanceof JsonElement) {
- obj.add("value", (JsonElement) value);
- } else {
- obj.addProperty("value", value.toString());
- }
- return obj;
- }
-
- /**
- * @return The action
- */
- public String getAction() {
- return action;
- }
-
- /**
- * @param action The action to set
- */
- public void setAction(String action) {
- this.action = action;
- }
-
- /**
- * @return The value
- */
- public Object getValue() {
- return value;
- }
-
- /**
- * @param value The value to set
- */
- public void setValue(Object value) {
- this.value = value;
- }
-
- }
-
- public static class ClickEvent {
-
- /**
- * Runs a command.
- *
- * @param command The command to run
- * @return The {@link MessageEvent}
- */
- public static MessageEvent runCommand(String command) {
- return new MessageEvent("run_command", command);
- }
-
- /**
- * Suggests a command by putting inserting it in chat.
- *
- * @param command The command to suggest
- * @return The {@link MessageEvent}
- */
- public static MessageEvent suggestCommand(String command) {
- return new MessageEvent("suggest_command", command);
- }
-
- /**
- * Requires web links to be enabled on the client.
- *
- * @param url The url to open
- * @return The {@link MessageEvent}
- */
- public static MessageEvent openURL(String url) {
- return new MessageEvent("open_url", url);
- }
-
- /**
- * Only used with written books.
- *
- * @param page The page to switch to
- * @return The {@link MessageEvent}
- */
- public static MessageEvent changePage(int page) {
- return new MessageEvent("change_page", page);
- }
-
- }
-
- public static class HoverEvent {
-
- /**
- * Shows text when you hover over it
- *
- * @param text The text to show
- * @return The {@link MessageEvent}
- */
- public static MessageEvent showText(String text) {
- return new MessageEvent("show_text", text);
- }
-
- /**
- * Shows text when you hover over it
- *
- * @param message The {@link JSONMessage} to show
- * @return The {@link MessageEvent}
- */
- public static MessageEvent showText(JSONMessage message) {
- JsonArray arr = new JsonArray();
- arr.add(new JsonPrimitive(""));
- arr.add(message.toJSON());
- return new MessageEvent("show_text", arr);
- }
-
- /**
- * Shows an achievement when you hover over it
- *
- * @param id The id of the achievement
- * @return The {@link MessageEvent}
- */
- public static MessageEvent showAchievement(String id) {
- return new MessageEvent("show_achievement", id);
- }
-
- }
-
- private static class ReflectionHelper {
-
- private static final String version;
- private static Class> craftPlayer;
- private static Constructor> chatComponentText;
- private static Class> packetPlayOutChat;
- private static Class> packetPlayOutTitle;
- private static Class> iChatBaseComponent;
- private static Class> titleAction;
- private static Field connection;
- private static MethodHandle GET_HANDLE;
- private static MethodHandle SEND_PACKET;
- private static MethodHandle STRING_TO_CHAT;
- private static Object enumActionTitle;
- private static Object enumActionSubtitle;
- private static Object enumChatMessage;
- private static Object enumActionbarMessage;
- private static boolean SETUP;
- private static int MAJOR_VER = -1;
-
- static {
- String[] split = Bukkit.getServer().getClass().getPackage().getName().split("\\.");
- version = split[split.length - 1];
-
- try {
- SETUP = true;
-
- MAJOR_VER = getVersion();
-
- craftPlayer = getClass("{obc}.entity.CraftPlayer");
- Method getHandle = craftPlayer.getMethod("getHandle");
- connection = getHandle.getReturnType().getField("playerConnection");
- Method sendPacket = connection.getType().getMethod("sendPacket", getClass("{nms}.Packet"));
-
- chatComponentText = getClass("{nms}.ChatComponentText").getConstructor(String.class);
-
- iChatBaseComponent = getClass("{nms}.IChatBaseComponent");
-
- Method stringToChat;
-
- if (MAJOR_VER < 8) {
- stringToChat = getClass("{nms}.ChatSerializer").getMethod("a", String.class);
- } else {
- stringToChat = getClass("{nms}.IChatBaseComponent$ChatSerializer").getMethod("a", String.class);
- }
-
- GET_HANDLE = MethodHandles.lookup().unreflect(getHandle);
- SEND_PACKET = MethodHandles.lookup().unreflect(sendPacket);
- STRING_TO_CHAT = MethodHandles.lookup().unreflect(stringToChat);
-
- packetPlayOutChat = getClass("{nms}.PacketPlayOutChat");
- packetPlayOutTitle = getClass("{nms}.PacketPlayOutTitle");
-
- titleAction = getClass("{nms}.PacketPlayOutTitle$EnumTitleAction");
-
- enumActionTitle = titleAction.getField("TITLE").get(null);
- enumActionSubtitle = titleAction.getField("SUBTITLE").get(null);
-
- if (MAJOR_VER >= 12) {
- Method getChatMessageType = getClass("{nms}.ChatMessageType").getMethod("a", byte.class);
-
- enumChatMessage = getChatMessageType.invoke(null, (byte) 1);
- enumActionbarMessage = getChatMessageType.invoke(null, (byte) 2);
- }
-
- } catch (Exception e) {
- e.printStackTrace();
- SETUP = false;
- }
-
- }
-
- static void sendPacket(Object packet, Player... players) {
- if (!SETUP) {
- throw new IllegalStateException("ReflectionHelper is not set up!");
- }
- if (packet == null) {
- return;
- }
-
- for (Player player : players) {
- try {
- SEND_PACKET.bindTo(connection.get(GET_HANDLE.bindTo(player).invoke())).invoke(packet);
- } catch (Throwable e) {
- System.err.println("Failed to send packet");
- e.printStackTrace();
- }
- }
-
- }
-
- private static void setType(Object object, byte type) {
- if (MAJOR_VER < 12) {
- set("b", object, type);
- return;
- }
-
- switch (type) {
- case 1:
- set("b", object, enumChatMessage);
- break;
- case 2:
- set("b", object, enumActionbarMessage);
- break;
- default:
- throw new IllegalArgumentException("type must be 1 or 2");
- }
- }
-
- static Object createActionbarPacket(String message) {
- if (!SETUP) {
- throw new IllegalStateException("ReflectionHelper is not set up!");
- }
- Object packet = createTextPacket(message);
- setType(packet, (byte) 2);
- return packet;
- }
-
- static Object createTextPacket(String message) {
- if (!SETUP) {
- throw new IllegalStateException("ReflectionHelper is not set up!");
- }
- try {
- Object packet = packetPlayOutChat.newInstance();
- set("a", packet, fromJson(message));
- setType(packet, (byte) 1);
- return packet;
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
-
- }
-
- static void sendTextPacket(String message, Player... players) {
- try {
- for (Player player : players) {
- Class chatTypeClass = getClass("{nms}.ChatMessageType");
- Constructor> constructor = packetPlayOutChat.getConstructor(getClass("{nms}.IChatBaseComponent"), chatTypeClass, UUID.class);
- Object packet = constructor.newInstance(fromJson(message), Enum.valueOf(chatTypeClass, "CHAT"), player.getUniqueId());
-
- Object handler = player.getClass().getMethod("getHandle").invoke(player);
- Object playerConnection = handler.getClass().getField("playerConnection").get(handler);
- playerConnection.getClass().getMethod("sendPacket", getClass("{nms}.Packet")).invoke(playerConnection, packet);
- }
- } catch (IllegalArgumentException | NoSuchMethodException | NoSuchFieldException | IllegalAccessException | InvocationTargetException | InstantiationException | ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
-
- static Object createTitlePacket(String message) {
- if (!SETUP) {
- throw new IllegalStateException("ReflectionHelper is not set up!");
- }
- try {
- return packetPlayOutTitle.getConstructor(titleAction, iChatBaseComponent).newInstance(enumActionTitle, fromJson(message));
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
-
- }
-
- static Object createSubtitlePacket(String message) {
- if (!SETUP) {
- throw new IllegalStateException("ReflectionHelper is not set up!");
- }
- try {
- return packetPlayOutTitle.getConstructor(titleAction, iChatBaseComponent).newInstance(enumActionSubtitle, fromJson(message));
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
-
- }
-
- static Object createTitleTimesPacket(int fadeIn, int stay, int fadeOut) {
- if (!SETUP) {
- throw new IllegalStateException("ReflectionHelper is not set up!");
- }
- try {
- return packetPlayOutTitle.getConstructor(int.class, int.class, int.class).newInstance(fadeIn, stay, fadeOut);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
-
- }
-
- /**
- * Creates a ChatComponentText from plain text
- *
- * @param message The text to convert to a chat component
- * @return The chat component
- */
- static Object componentText(String message) {
- if (!SETUP) {
- throw new IllegalStateException("ReflectionHelper is not set up!");
- }
- try {
- return chatComponentText.newInstance(message);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
-
- }
-
- /**
- * Attempts to convert a String representing a JSON message into a usable object
- *
- * @param json The JSON to attempt to parse
- * @return The object representing the text in JSON form, or null
if something went wrong converting the String to JSON data
- */
- static Object fromJson(String json) {
- if (!SETUP) {
- throw new IllegalStateException("ReflectionHelper is not set up!");
- }
- if (!json.trim().startsWith("{")) {
- return componentText(json);
- }
-
- try {
- return STRING_TO_CHAT.invoke(json);
- } catch (Throwable e) {
- e.printStackTrace();
- return null;
- }
-
- }
-
- /**
- * Returns a class with the given package and name. This method replaces {nms}
with net.minecraft.server.[version]
and {obc}
with org.bukkit.craft.[version]
- *
- *
- * Example:
- *
- *
- * Class> entityPlayer = ReflectionHelper.getClass("{nms}.EntityPlayer");
- *
- *
- * @param path The path to the {@link Class}
- * @return The class
- * @throws ClassNotFoundException If the class was not found
- */
- static Class> getClass(String path) throws ClassNotFoundException {
- if (!SETUP) {
- throw new IllegalStateException("ReflectionHelper is not set up!");
- }
- return Class.forName(path.replace("{nms}", "net.minecraft.server." + version).replace("{obc}", "org.bukkit.craftbukkit." + version));
- }
-
- /**
- * Sets a field with the given name on an object to the value specified
- *
- * @param field The name of the field to change
- * @param obj The object to change the field of
- * @param value The new value to set
- */
- static void set(String field, Object obj, Object value) {
- try {
- Field f = obj.getClass().getDeclaredField(field);
- f.setAccessible(true);
- f.set(obj, value);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public static String getStringVersion() {
- return version;
- }
-
- static int getVersion() {
- if (!SETUP) {
- throw new IllegalStateException("ReflectionHelper is not set up!");
- }
- try {
- return Integer.parseInt(version.split("_")[1]);
- } catch (NumberFormatException e) {
- e.printStackTrace();
- return 10;
- }
-
- }
-
- }
-
- /**
- * Defines a section of the message, and represents the format that all JSON messages must follow in Minecraft.
- *
- *
- * Reference
- *
- * @author Rayzr
- */
- public static class MessagePart {
-
- private final List styles = new ArrayList<>();
- private MessageEvent onClick;
- private MessageEvent onHover;
- private ChatColor color;
- private String text;
-
- public MessagePart(String text) {
- this.text = text == null ? "null" : text;
- }
-
- /**
- * Converts this {@link MessagePart} into a {@link JsonObject}
- *
- * @return The Minecraft-compatible {@link JsonObject}
- */
- public JsonObject toJSON() {
- Objects.requireNonNull(text);
-
- JsonObject obj = new JsonObject();
- obj.addProperty("text", text);
-
- if (color != null) {
- obj.addProperty("color", color.name().toLowerCase());
- }
-
- for (ChatColor style : styles) {
- obj.addProperty(stylesToNames.get(style), true);
- }
-
- if (onClick != null) {
- obj.add("clickEvent", onClick.toJSON());
- }
-
- if (onHover != null) {
- obj.add("hoverEvent", onHover.toJSON());
- }
-
- return obj;
-
- }
-
- /**
- * @return This {@link MessagePart} in legacy-style color/formatting codes
- */
- public String toLegacy() {
- StringBuilder output = new StringBuilder();
- if (color != null) {
- output.append(color.toString());
- }
- styles.stream()
- .map(ChatColor::toString)
- .forEach(output::append);
-
- return output.append(text).toString();
- }
-
- /**
- * @return The click event bound
- */
- public MessageEvent getOnClick() {
- return onClick;
- }
-
- /**
- * @param onClick The new click event to bind
- */
- public void setOnClick(MessageEvent onClick) {
- this.onClick = onClick;
- }
-
- /**
- * @return The hover event bound
- */
- public MessageEvent getOnHover() {
- return onHover;
- }
-
- /**
- * @param onHover The new hover event to bind
- */
- public void setOnHover(MessageEvent onHover) {
- this.onHover = onHover;
- }
-
- /**
- * @return The color
- */
- public ChatColor getColor() {
- return color;
- }
-
- /**
- * @param color The color to set
- */
- public void setColor(ChatColor color) {
- if (!color.isColor()) {
- throw new IllegalArgumentException(color.name() + " is not a color!");
- }
- this.color = color;
- }
-
- /**
- * @return The list of styles
- */
- public List getStyles() {
- return styles;
- }
-
- /**
- * @param style The new style to add
- */
- public void addStyle(ChatColor style) {
- if (style == null) {
- throw new IllegalArgumentException("Style cannot be null!");
- }
- if (!style.isFormat()) {
- throw new IllegalArgumentException(color.name() + " is not a style!");
- }
- styles.add(style);
- }
-
- /**
- * @return The raw text
- */
- public String getText() {
- return text;
- }
-
- /**
- * @param text The raw text to set
- */
- public void setText(String text) {
- this.text = text;
- }
-
- }
-
-}
\ No newline at end of file