Update JSON message builder/parser

Preparing for item preview in shop info
This commit is contained in:
Eric 2018-07-26 00:02:06 +02:00
parent 381de24362
commit 9a70df5e01
5 changed files with 207 additions and 75 deletions

View File

@ -21,7 +21,6 @@ import de.epiceric.shopchest.listeners.ShopInteractListener;
import de.epiceric.shopchest.listeners.ShopItemListener;
import de.epiceric.shopchest.listeners.ShopUpdateListener;
import de.epiceric.shopchest.listeners.WorldGuardListener;
import de.epiceric.shopchest.nms.JsonBuilder;
import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.shop.Shop.ShopType;
import de.epiceric.shopchest.sql.Database;
@ -334,8 +333,7 @@ public class ShopChest extends JavaPlugin {
for (Player p : getServer().getOnlinePlayers()) {
if (p.hasPermission(Permissions.UPDATE_NOTIFICATION)) {
JsonBuilder jb = new JsonBuilder(ShopChest.this, LanguageUtils.getMessage(Message.UPDATE_AVAILABLE, new Replacement(Placeholder.VERSION, latestVersion)), LanguageUtils.getMessage(Message.UPDATE_CLICK_TO_DOWNLOAD), downloadLink);
jb.sendJson(p);
Utils.sendUpdateMessage(ShopChest.this, p);
}
}

View File

@ -12,7 +12,6 @@ import de.epiceric.shopchest.event.ShopRemoveAllEvent;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.language.Message;
import de.epiceric.shopchest.language.Replacement;
import de.epiceric.shopchest.nms.JsonBuilder;
import de.epiceric.shopchest.shop.Shop;
import de.epiceric.shopchest.utils.Callback;
import de.epiceric.shopchest.utils.ClickType;
@ -155,8 +154,7 @@ class ShopCommandExecutor implements CommandExecutor {
plugin.setUpdateNeeded(true);
if (sender instanceof Player) {
JsonBuilder jb = new JsonBuilder(plugin, LanguageUtils.getMessage(Message.UPDATE_AVAILABLE, new Replacement(Placeholder.VERSION, uc.getVersion())), LanguageUtils.getMessage(Message.UPDATE_CLICK_TO_DOWNLOAD), uc.getLink());
jb.sendJson((Player) sender);
Utils.sendUpdateMessage(plugin, (Player) sender);
} else {
sender.sendMessage(LanguageUtils.getMessage(Message.UPDATE_AVAILABLE, new Replacement(Placeholder.VERSION, uc.getVersion())));
}

View File

@ -11,9 +11,9 @@ import de.epiceric.shopchest.config.Placeholder;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.language.Message;
import de.epiceric.shopchest.language.Replacement;
import de.epiceric.shopchest.nms.JsonBuilder;
import de.epiceric.shopchest.utils.Callback;
import de.epiceric.shopchest.utils.Permissions;
import de.epiceric.shopchest.utils.Utils;
public class NotifyPlayerOnJoinListener implements Listener {
@ -29,8 +29,7 @@ public class NotifyPlayerOnJoinListener implements Listener {
if (plugin.isUpdateNeeded()) {
if (p.hasPermission(Permissions.UPDATE_NOTIFICATION)) {
JsonBuilder jb = new JsonBuilder(plugin, LanguageUtils.getMessage(Message.UPDATE_AVAILABLE, new Replacement(Placeholder.VERSION, plugin.getLatestVersion())), LanguageUtils.getMessage(Message.UPDATE_CLICK_TO_DOWNLOAD), plugin.getDownloadLink());
jb.sendJson(p);
Utils.sendUpdateMessage(plugin, p);
}
}

View File

@ -7,19 +7,113 @@ import org.bukkit.entity.Player;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JsonBuilder {
private List<String> extras = new ArrayList<>();
public static class Part {
private String value;
public Part() {
this("");
}
public Part(Object value) {
if (value instanceof CharSequence) {
this.value = "\"" + value + "\"";
} else {
this.value = String.valueOf(value);
}
}
@Override
public String toString() {
return value;
}
public PartArray toArray() {
return new PartArray(this);
}
public PartMap toMap() {
PartMap map = new PartMap();
map.setValue("text", new Part());
map.setValue("extra", toArray());
return map;
}
}
public static class PartMap extends Part {
private Map<String, Part> values = new HashMap<>();
public PartMap() {
}
public PartMap(Map<String, Part> values) {
this.values.putAll(values);
}
public void setValue(String key, Part value) {
values.put(key, value);
}
public void removeValue(String key) {
values.remove(key);
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(",", "{", "}");
values.forEach((key, value) -> joiner.add("\"" + key + "\":" + value.toString()));
return joiner.toString();
}
@Override
public PartMap toMap() {
return this;
}
}
public static class PartArray extends Part {
private List<Part> parts = new ArrayList<>();
public PartArray(Part... parts) {
this.parts.addAll(Arrays.asList(parts));
}
public void addPart(Part part) {
parts.add(part);
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(",", "[", "]");
parts.forEach(part -> joiner.add(part.toString()));
return joiner.toString();
}
@Override
public PartArray toArray() {
return this;
}
}
private static final Pattern PART_PATTERN = Pattern.compile("(([§][a-fA-Fl-oL-OkK0-9])+)([^§]*)");
private Part rootPart;
private ShopChest plugin;
private Class<?> iChatBaseComponentClass = Utils.getNMSClass("IChatBaseComponent");
private Class<?> packetPlayOutChatClass = Utils.getNMSClass("PacketPlayOutChat");
private Class<?> chatSerializerClass;
public JsonBuilder(ShopChest plugin, String text, String hoverText, String downloadLink) {
public JsonBuilder(ShopChest plugin) {
this.plugin = plugin;
if (Utils.getServerVersion().equals("v1_8_R1")) {
@ -38,87 +132,99 @@ public class JsonBuilder {
return;
}
}
parse(text, hoverText, downloadLink);
}
private JsonBuilder parse(String text, String hoverText, String downloadLink) {
String regex = "[&§]{1}([a-fA-Fl-oL-O0-9]){1}";
text = text.replaceAll(regex, "§$1");
if (!Pattern.compile(regex).matcher(text).find()) {
withText(text).withHoverEvent(hoverText).withClickEvent(downloadLink);
return this;
public void parse(String text) {
Matcher matcher = PART_PATTERN.matcher(text);
if (!matcher.find()) {
rootPart = new Part(text);
return;
}
String[] words = text.split(regex);
int index = words[0].length();
for (String word : words) {
try {
if (index != words[0].length())
withText(word).withColor("§" + text.charAt(index - 1)).withHoverEvent(hoverText).withClickEvent(downloadLink);
} catch (Exception e) {}
index += word.length() + 2;
matcher.reset();
PartArray array = new PartArray();
int lastEndIndex = 0;
while (matcher.find()) {
int startIndex = matcher.start();
int endIndex = matcher.end();
if (lastEndIndex != startIndex) {
String betweenMatches = text.substring(lastEndIndex, startIndex);
array.addPart(new Part(betweenMatches));
}
String format = matcher.group(1);
String value = matcher.group(3);
PartMap part = new PartMap();
part.setValue("text", new Part(value));
String[] formats = format.split("§");
for (String f : formats) {
switch (f.toLowerCase()) {
case "":
break;
case "k":
part.setValue("obuscated", new Part(true));
break;
case "l":
part.setValue("bold", new Part(true));
break;
case "m":
part.setValue("strikethrough", new Part(true));
break;
case "n":
part.setValue("underlined", new Part(true));
break;
case "o":
part.setValue("italic", new Part(true));
break;
case "r":
part.removeValue("obfuscated");
part.removeValue("bold");
part.removeValue("strikethrough");
part.removeValue("underlined");
part.removeValue("italic");
part.removeValue("color");
break;
default:
part.setValue("color", new Part(ChatColor.getByChar(f).name().toLowerCase()));
}
}
array.addPart(part);
lastEndIndex = endIndex;
}
return this;
}
private JsonBuilder withText(String text) {
extras.add("{\"text\":\"" + text + "\"}");
return this;
}
private JsonBuilder withColor(ChatColor color) {
String c = color.name().toLowerCase();
addSegment(color.isColor() ? "\"color\":\"" + c + "\"" : "\"" + c + "\"" + ":true");
return this;
}
private JsonBuilder withColor(String color) {
while (color.length() != 1) color = color.substring(1).trim();
withColor(ChatColor.getByChar(color));
return this;
}
private JsonBuilder withClickEvent(String value) {
addSegment("\"clickEvent\":{\"action\":\"open_url"
+ "\",\"value\":\"" + value + "\"}");
return this;
}
private JsonBuilder withHoverEvent(String value) {
addSegment("\"hoverEvent\":{\"action\":\"show_text"
+ "\",\"value\":\"" + value + "\"}");
return this;
}
private void addSegment(String segment) {
String lastText = extras.get(extras.size() - 1);
lastText = lastText.substring(0, lastText.length() - 1)
+ "," + segment + "}";
extras.remove(extras.size() - 1);
extras.add(lastText);
rootPart = array;
}
@Override
public String toString() {
if (extras.size() <= 1) return extras.size() == 0 ? "{\"text\":\"\"}" : extras.get(0);
String text = extras.get(0).substring(0, extras.get(0).length() - 1) + ",\"extra\":[";
extras.remove(0);
for (String extra : extras)
text = text + extra + ",";
text = text.substring(0, text.length() - 1) + "]}";
return text;
return rootPart.toString();
}
public void sendJson(Player p) {
public Part getRootPart() {
return rootPart;
}
public void setRootPart(Part rootPart) {
this.rootPart = rootPart;
}
public void sendJson(Player p) {
try {
Object iChatBaseComponent = chatSerializerClass.getMethod("a", String.class).invoke(null, toString());
Object packetPlayOutChat = packetPlayOutChatClass.getConstructor(iChatBaseComponentClass).newInstance(iChatBaseComponent);
Utils.sendPacket(plugin, packetPlayOutChat, p);
} catch (InstantiationException | InvocationTargetException |
IllegalAccessException | NoSuchMethodException e) {
plugin.getLogger().severe("Failed to send JSON with reflection");
plugin.debug("Failed to send JSON with reflection");
plugin.debug("Failed to send JSON with reflection: " + toString());
plugin.debug(e);
}
}

View File

@ -3,8 +3,13 @@ package de.epiceric.shopchest.utils;
import com.intellectualcrafters.plot.flag.Flag;
import com.intellectualcrafters.plot.object.Plot;
import de.epiceric.shopchest.ShopChest;
import de.epiceric.shopchest.config.Placeholder;
import de.epiceric.shopchest.external.PlotSquaredShopFlag;
import de.epiceric.shopchest.language.LanguageUtils;
import de.epiceric.shopchest.language.Message;
import de.epiceric.shopchest.language.Replacement;
import de.epiceric.shopchest.nms.CustomBookMeta;
import de.epiceric.shopchest.nms.JsonBuilder;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@ -23,6 +28,7 @@ import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Utils {
@ -307,6 +313,31 @@ public class Utils {
return true;
}
/**
* Send a clickable update notification to the given player.
* @param plugin An instance of the {@link ShopChest} plugin
* @param p The player to receive the notification
*/
public static void sendUpdateMessage(ShopChest plugin, Player p) {
JsonBuilder jb = new JsonBuilder(plugin);
Map<String, JsonBuilder.Part> hoverEvent = new HashMap<>();
hoverEvent.put("action", new JsonBuilder.Part("show_text"));
hoverEvent.put("value", new JsonBuilder.Part(LanguageUtils.getMessage(Message.UPDATE_CLICK_TO_DOWNLOAD)));
Map<String, JsonBuilder.Part> clickEvent = new HashMap<>();
clickEvent.put("action", new JsonBuilder.Part("open_url"));
clickEvent.put("value", new JsonBuilder.Part(plugin.getDownloadLink()));
jb.parse(LanguageUtils.getMessage(Message.UPDATE_AVAILABLE, new Replacement(Placeholder.VERSION, plugin.getLatestVersion())));
JsonBuilder.PartMap rootPart = jb.getRootPart().toMap();
rootPart.setValue("hoverEvent", new JsonBuilder.PartMap(hoverEvent));
rootPart.setValue("clickEvent", new JsonBuilder.PartMap(clickEvent));
jb.setRootPart(rootPart);
jb.sendJson(p);
}
/**
* @param className Name of the class
* @return Class in {@code net.minecraft.server.[VERSION]} package with the specified name or {@code null} if the class was not found