public class JSONMessage { /* The MIT License (MIT) * * Copyright (c) 2013-2014 Max Kreminski * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. */ private final List<MessagePart> messageParts; private String jsonString; private boolean dirty; private Class<?> nmsChatSerializer = ReflectionUtil.getNMSClass("ChatSerializer"); private Class<?> nmsTagCompound = ReflectionUtil.getNMSClass("NBTTagCompound"); private Class<?> nmsPacketPlayOutChat = ReflectionUtil.getNMSClass("PacketPlayOutChat"); private Class<?> nmsAchievement = ReflectionUtil.getNMSClass("Achievement"); private Class<?> nmsStatistic = ReflectionUtil.getNMSClass("Statistic"); private Class<?> nmsItemStack = ReflectionUtil.getNMSClass("ItemStack"); private Class<?> obcStatistic = ReflectionUtil.getOBCClass("CraftStatistic"); private Class<?> obcItemStack = ReflectionUtil.getOBCClass("inventory.CraftItemStack"); public JSONMessage(final String firstPartText) { messageParts = new ArrayList<MessagePart>(); messageParts.add(new MessagePart(firstPartText)); jsonString = null; dirty = false; } public JSONMessage() { messageParts = new ArrayList<MessagePart>(); messageParts.add(new MessagePart()); jsonString = null; dirty = false; } public JSONMessage text(String text) { MessagePart latest = latest(); if (latest.hasText()) { throw new IllegalStateException("text for this message part is already set"); } latest.text = text; dirty = true; return this; } public JSONMessage color(final ChatColor color) { if (!color.isColor()) { throw new IllegalArgumentException(color.name() + " is not a color"); } latest().color = color; dirty = true; return this; } public JSONMessage style(ChatColor... styles) { for (final ChatColor style : styles) { if (!style.isFormat()) { throw new IllegalArgumentException(style.name() + " is not a style"); } } latest().styles.addAll(Arrays.asList(styles)); dirty = true; return this; } public JSONMessage file(final String path) { onClick("open_file", path); return this; } public JSONMessage link(final String url) { onClick("open_url", url); return this; } public JSONMessage suggest(final String command) { onClick("suggest_command", command); return this; } public JSONMessage command(final String command) { onClick("run_command", command); return this; } public JSONMessage achievementTooltip(final String name) { onHover("show_achievement", "achievement." + name); return this; } public JSONMessage achievementTooltip(final Achievement which) { try { Object achievement = ReflectionUtil.getMethod(obcStatistic, "getNMSAchievement").invoke(null, which); return achievementTooltip( (String) ReflectionUtil.getField(nmsAchievement, "name").get(achievement)); } catch (Exception e) { e.printStackTrace(); return this; } } public JSONMessage statisticTooltip(final Statistic which) { Type type = which.getType(); if (type != Type.UNTYPED) { throw new IllegalArgumentException( "That statistic requires an additional " + type + " parameter!"); } try { Object statistic = ReflectionUtil.getMethod(obcStatistic, "getNMSStatistic").invoke(null, which); return achievementTooltip( (String) ReflectionUtil.getField(nmsStatistic, "name").get(statistic)); } catch (Exception e) { e.printStackTrace(); return this; } } public JSONMessage statisticTooltip(final Statistic which, Material item) { Type type = which.getType(); if (type == Type.UNTYPED) { throw new IllegalArgumentException("That statistic needs no additional parameter!"); } if ((type == Type.BLOCK && item.isBlock()) || type == Type.ENTITY) { throw new IllegalArgumentException( "Wrong parameter type for that statistic - needs " + type + "!"); } try { Object statistic = ReflectionUtil.getMethod(obcStatistic, "getMaterialStatistic").invoke(null, which, item); return achievementTooltip( (String) ReflectionUtil.getField(nmsStatistic, "name").get(statistic)); } catch (Exception e) { e.printStackTrace(); return this; } } public JSONMessage statisticTooltip(final Statistic which, EntityType entity) { Type type = which.getType(); if (type == Type.UNTYPED) { throw new IllegalArgumentException("That statistic needs no additional parameter!"); } if (type != Type.ENTITY) { throw new IllegalArgumentException( "Wrong parameter type for that statistic - needs " + type + "!"); } try { Object statistic = ReflectionUtil.getMethod(obcStatistic, "getEntityStatistic").invoke(null, which, entity); return achievementTooltip( (String) ReflectionUtil.getField(nmsStatistic, "name").get(statistic)); } catch (Exception e) { e.printStackTrace(); return this; } } public JSONMessage itemTooltip(final String itemJSON) { onHover("show_item", itemJSON); return this; } public JSONMessage itemTooltip(final ItemStack itemStack) { try { Object nmsItem = ReflectionUtil.getMethod(obcItemStack, "asNMSCopy", ItemStack.class) .invoke(null, itemStack); return itemTooltip( ReflectionUtil.getMethod(nmsItemStack, "save") .invoke(nmsItem, nmsTagCompound.newInstance()) .toString()); } catch (Exception e) { e.printStackTrace(); return this; } } public JSONMessage tooltip(final String text) { return tooltip(text.split("\\n")); } public JSONMessage tooltip(final List<String> lines) { return tooltip((String[]) lines.toArray()); } public JSONMessage tooltip(final String... lines) { if (lines.length == 1) { onHover("show_text", lines[0]); } else { itemTooltip(makeMultilineTooltip(lines)); } return this; } public JSONMessage then(final Object obj) { if (!latest().hasText()) { throw new IllegalStateException("previous message part has no text"); } messageParts.add(new MessagePart(obj.toString())); dirty = true; return this; } public JSONMessage then() { if (!latest().hasText()) { throw new IllegalStateException("previous message part has no text"); } messageParts.add(new MessagePart()); dirty = true; return this; } public String toJSONString() { if (!dirty && jsonString != null) { return jsonString; } StringWriter string = new StringWriter(); JsonWriter json = new JsonWriter(string); try { if (messageParts.size() == 1) { latest().writeJson(json); } else { json.beginObject().name("text").value("").name("extra").beginArray(); for (final MessagePart part : messageParts) { part.writeJson(json); } json.endArray().endObject(); json.close(); } } catch (Exception e) { throw new RuntimeException("invalid message"); } jsonString = string.toString(); dirty = false; return jsonString; } public void send(Player player) { try { Object handle = ReflectionUtil.getHandle(player); Object connection = ReflectionUtil.getField(handle.getClass(), "playerConnection").get(handle); Object serialized = ReflectionUtil.getMethod(nmsChatSerializer, "a", String.class) .invoke(null, toJSONString()); Object packet = nmsPacketPlayOutChat .getConstructor(ReflectionUtil.getNMSClass("IChatBaseComponent")) .newInstance(serialized); ReflectionUtil.getMethod(connection.getClass(), "sendPacket").invoke(connection, packet); } catch (Exception e) { e.printStackTrace(); } } public void send(CommandSender sender) { if (sender instanceof Player) { send((Player) sender); } else { sender.sendMessage(toOldMessageFormat()); } } public void send(final Iterable<? extends CommandSender> senders) { for (final CommandSender sender : senders) { send(sender); } } public void sendToSpectators(List<SpectatorUser> users) { for (final SpectatorUser su : users) { send(su.getPlayer()); } } public void send(List<User> users) { for (final User u : users) { send(u.getPlayer()); } } public String toOldMessageFormat() { StringBuilder result = new StringBuilder(); for (MessagePart part : messageParts) { result.append(part.color).append(part.text); } return result.toString(); } private MessagePart latest() { return messageParts.get(messageParts.size() - 1); } private String makeMultilineTooltip(final String[] lines) { StringWriter string = new StringWriter(); JsonWriter json = new JsonWriter(string); try { json.beginObject().name("id").value(1); json.name("tag").beginObject().name("display").beginObject(); json.name("Name").value("\\u00A7f" + lines[0].replace("\"", "\\\"")); json.name("Lore").beginArray(); for (int i = 1; i < lines.length; i++) { final String line = lines[i]; json.value(line.isEmpty() ? " " : line.replace("\"", "\\\"")); } json.endArray().endObject().endObject().endObject(); json.close(); } catch (Exception e) { throw new RuntimeException("invalid tooltip"); } return string.toString(); } private void onClick(final String name, final String data) { final MessagePart latest = latest(); latest.clickActionName = name; latest.clickActionData = data; dirty = true; } private void onHover(final String name, final String data) { final MessagePart latest = latest(); latest.hoverActionName = name; latest.hoverActionData = data; dirty = true; } }