diff --git a/src/main/java/com/songoda/ultimatekits/UltimateKits.java b/src/main/java/com/songoda/ultimatekits/UltimateKits.java index f392d8a..4f9e621 100644 --- a/src/main/java/com/songoda/ultimatekits/UltimateKits.java +++ b/src/main/java/com/songoda/ultimatekits/UltimateKits.java @@ -13,6 +13,7 @@ import com.songoda.ultimatekits.key.Key; import com.songoda.ultimatekits.key.KeyManager; import com.songoda.ultimatekits.kit.*; import com.songoda.ultimatekits.utils.Debugger; +import com.songoda.ultimatekits.utils.ItemSerializer; import com.songoda.ultimatekits.utils.SettingsManager; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -42,6 +43,8 @@ public class UltimateKits extends JavaPlugin { private KeyManager keyManager; private HologramHandler hologramHandler; private DisplayItemHandler displayItemHandler; + + private ItemSerializer itemSerializer; /** * Grab instance of UltimateKits @@ -101,6 +104,13 @@ public class UltimateKits extends JavaPlugin { this.keyManager = new KeyManager(); this.commandManager = new CommandManager(this); this.hologramHandler = new HologramHandler(this); + + try { + this.itemSerializer = new ItemSerializer(); + } catch (NoSuchMethodException | SecurityException | ClassNotFoundException e) { + console.sendMessage(TextComponent.formatText("&cCould not load the serialization class! Please report this error.")); + e.printStackTrace(); + } this.loadFromFile(); @@ -336,6 +346,7 @@ public class UltimateKits extends JavaPlugin { public ConfigWrapper getDataFile() { return dataFile; } + public SettingsManager getSettingsManager() { return settingsManager; @@ -348,6 +359,15 @@ public class UltimateKits extends JavaPlugin { public HologramHandler getHologramHandler() { return hologramHandler; } + + /** + * Grab instance of the item serializer + * + * @return instance of ItemSerializer + */ + public ItemSerializer getItemSerializer() { + return this.itemSerializer; + } public References getReferences() { return references; diff --git a/src/main/java/com/songoda/ultimatekits/conversion/Convert.java b/src/main/java/com/songoda/ultimatekits/conversion/Convert.java index 6e11119..a0f4478 100644 --- a/src/main/java/com/songoda/ultimatekits/conversion/Convert.java +++ b/src/main/java/com/songoda/ultimatekits/conversion/Convert.java @@ -53,7 +53,7 @@ public class Convert { for (String kit : kits) { List serializedItems = new ArrayList<>(); for (ItemStack item : hook.getItems(kit)) { - serializedItems.add(Methods.serializeItemStackToJson(item)); + serializedItems.add(instance.getItemSerializer().serializeItemStackToJson(item)); } instance.getKitFile().getConfig().set("Kits." + kit + ".items", serializedItems); instance.getKitFile().getConfig().set("Kits." + kit + ".delay", hook.getDelay(kit)); diff --git a/src/main/java/com/songoda/ultimatekits/conversion/hooks/DefaultDeprecatedHook.java b/src/main/java/com/songoda/ultimatekits/conversion/hooks/DefaultDeprecatedHook.java index 54fc344..696b6be 100644 --- a/src/main/java/com/songoda/ultimatekits/conversion/hooks/DefaultDeprecatedHook.java +++ b/src/main/java/com/songoda/ultimatekits/conversion/hooks/DefaultDeprecatedHook.java @@ -1,7 +1,7 @@ package com.songoda.ultimatekits.conversion.hooks; +import com.songoda.ultimatekits.UltimateKits; import com.songoda.ultimatekits.conversion.Hook; -import com.songoda.ultimatekits.utils.Methods; import org.bukkit.inventory.ItemStack; import java.util.HashSet; @@ -15,7 +15,7 @@ public class DefaultDeprecatedHook implements Hook { for (Kits kit : Kits.values()) { if (!kit.name().equalsIgnoreCase(kitName)) continue; for (String string : kit.items) { - items.add(Methods.deserializeItemStack(string)); + items.add(UltimateKits.getInstance().getItemSerializer().deserializeItemStack(string)); } } diff --git a/src/main/java/com/songoda/ultimatekits/conversion/hooks/DefaultHook.java b/src/main/java/com/songoda/ultimatekits/conversion/hooks/DefaultHook.java index c0c6c32..b861743 100644 --- a/src/main/java/com/songoda/ultimatekits/conversion/hooks/DefaultHook.java +++ b/src/main/java/com/songoda/ultimatekits/conversion/hooks/DefaultHook.java @@ -1,7 +1,7 @@ package com.songoda.ultimatekits.conversion.hooks; +import com.songoda.ultimatekits.UltimateKits; import com.songoda.ultimatekits.conversion.Hook; -import com.songoda.ultimatekits.utils.Methods; import org.bukkit.inventory.ItemStack; import java.util.HashSet; @@ -15,7 +15,7 @@ public class DefaultHook implements Hook { for (Kits kit : Kits.values()) { if (!kit.name().equalsIgnoreCase(kitName)) continue; for (String string : kit.items) { - items.add(Methods.deserializeItemStackFromJson(string)); + items.add(UltimateKits.getInstance().getItemSerializer().deserializeItemStackFromJson(string)); } } diff --git a/src/main/java/com/songoda/ultimatekits/kit/KitItem.java b/src/main/java/com/songoda/ultimatekits/kit/KitItem.java index e7f0710..658c191 100644 --- a/src/main/java/com/songoda/ultimatekits/kit/KitItem.java +++ b/src/main/java/com/songoda/ultimatekits/kit/KitItem.java @@ -7,7 +7,6 @@ import com.songoda.ultimatekits.kit.type.KitContent; import com.songoda.ultimatekits.kit.type.KitContentCommand; import com.songoda.ultimatekits.kit.type.KitContentEconomy; import com.songoda.ultimatekits.kit.type.KitContentItem; -import com.songoda.ultimatekits.utils.Methods; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; @@ -33,7 +32,7 @@ public class KitItem { } else if (line.startsWith("/")) { this.content = new KitContentCommand(line.substring(1)); } else { - this.content = new KitContentItem(Methods.deserializeLegacyItemStack(line)); + this.content = new KitContentItem(UltimateKits.getInstance().getItemSerializer().deserializeLegacyItemStack(line)); } } diff --git a/src/main/java/com/songoda/ultimatekits/kit/type/KitContentItem.java b/src/main/java/com/songoda/ultimatekits/kit/type/KitContentItem.java index cac3129..385f97f 100644 --- a/src/main/java/com/songoda/ultimatekits/kit/type/KitContentItem.java +++ b/src/main/java/com/songoda/ultimatekits/kit/type/KitContentItem.java @@ -1,5 +1,6 @@ package com.songoda.ultimatekits.kit.type; +import com.songoda.ultimatekits.UltimateKits; import com.songoda.ultimatekits.utils.Methods; import org.bukkit.inventory.ItemStack; @@ -20,7 +21,7 @@ public class KitContentItem implements KitContent { @Override public String getSerialized() { if (serialized != null) return serialized; - serialized = Methods.serializeItemStackToJson(itemStack); + serialized = UltimateKits.getInstance().getItemSerializer().serializeItemStackToJson(itemStack); return serialized; } diff --git a/src/main/java/com/songoda/ultimatekits/utils/ItemSerializer.java b/src/main/java/com/songoda/ultimatekits/utils/ItemSerializer.java new file mode 100644 index 0000000..9227c2f --- /dev/null +++ b/src/main/java/com/songoda/ultimatekits/utils/ItemSerializer.java @@ -0,0 +1,257 @@ +package com.songoda.ultimatekits.utils; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import com.songoda.arconix.plugin.Arconix; +import com.songoda.ultimatekits.UltimateKits; + +public class ItemSerializer { + + // classes needed for reflections + + private Class classMojangsonParser = Class.forName(formatNMS("net.minecraft.server.NMS.MojangsonParser")); + private Class classItemStack = Class.forName(formatNMS("net.minecraft.server.NMS.ItemStack")); + private Class classCraftItemStack = Class.forName(formatNMS("org.bukkit.craftbukkit.NMS.inventory.CraftItemStack")); + private Class classNBTTagCompound = Class.forName(formatNMS("net.minecraft.server.NMS.NBTTagCompound")); + private Class classBukkitItemStack = Class.forName("org.bukkit.inventory.ItemStack"); + + // reflected methods + + private Method methodParseString; + private Method methodToItemStack; + private Method methodTobItemStack; + private Method methodTocItemStack; + private Method methodSaveTagToStack; + private Method methodToString; + + /** + * Initializes all reflection methods + * + * @throws NoSuchMethodException + * @throws SecurityException + * @throws ClassNotFoundException + */ + public ItemSerializer() throws NoSuchMethodException, SecurityException, ClassNotFoundException { + methodParseString = classMojangsonParser.getMethod("parse", String.class); + methodToItemStack = classItemStack.getMethod("a", classNBTTagCompound); + methodTobItemStack = classCraftItemStack.getMethod("asBukkitCopy", classItemStack); + + methodTocItemStack = classCraftItemStack.getDeclaredMethod("asNMSCopy", classBukkitItemStack); + methodSaveTagToStack = classItemStack.getMethod("save", classNBTTagCompound); + methodToString = classNBTTagCompound.getMethod("toString"); + } + + /** + * Inserts the version declaration for any string containing NMS + * + * @param s the string to format, must contain NMS. + * @return formatted string + */ + private String formatNMS(String s) { + String packageName = Bukkit.getServer().getClass().getPackage().getName(); + String nmsVersion = packageName.substring(packageName.lastIndexOf('.') + 1); + return s.replace("NMS", nmsVersion); + } + + /** + * Deserializes a JSON String + * + * @param jsonString the JSON String to parse + * @return the deserialized ItemStack + */ + public ItemStack deserializeItemStackFromJson(String jsonString) { + try { + Object nbtTagCompound = methodParseString.invoke(null, jsonString); + Object citemStack = methodToItemStack.invoke(null, nbtTagCompound); + + return (ItemStack) methodTobItemStack.invoke(null, citemStack); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + } + + /** + * Serializes an item stack + * + * @param itemStack the ItemStack to parse + * @return condensed JSON String + */ + public String serializeItemStackToJson(ItemStack itemStack) { + try { + Object citemStack = methodTocItemStack.invoke(null, itemStack); + Object nbtTagCompoundObject = classNBTTagCompound.newInstance(); + + methodSaveTagToStack.invoke(citemStack, nbtTagCompoundObject); + + return (String) methodToString.invoke(nbtTagCompoundObject); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + + /** + * Deserializes a string to an item stack, support both formats + * + * @param string the formatted string + * @return the deserialized ItemStack + */ + public ItemStack deserializeLegacyItemStack(String string) { + if(string.contains("{")) { + // string is json + return deserializeItemStackFromJson(string); + } + // old format + return deserializeItemStack(string); + } + + /** + * This method is not able to handle skulls or in general nbt tags. + * Method is still existing for converting purposes. + * + * @deprecated use {@link #serializeItemStackToJson(ItemStack is)} instead. + */ + @Deprecated + public ItemStack deserializeItemStack(String string) { + string = string.replace("&", "§"); + String[] splited = string.split("\\s+"); + + String[] val = splited[0].split(":"); + ItemStack item = new ItemStack(Material.valueOf(val[0])); + + if (item.getType() == Material.PLAYER_HEAD) { + item = new ItemStack(Material.PLAYER_HEAD, 1, (byte) 3); + } + + ItemMeta meta = item.getItemMeta(); + + if (val.length == 2) { + item.setDurability(Short.parseShort(val[1])); + } + if (splited.length >= 2) { + if (Arconix.pl().getApi().doMath().isNumeric(splited[1])) { + item.setAmount(Integer.parseInt(splited[1])); + } + + for (String st : splited) { + String str = unfixLine(st); + if (!str.contains(":")) continue; + String[] ops = str.split(":", 2); + + String option = ops[0]; + String value = ops[1]; + + if (Enchantment.getByName(option.replace(" ", "_").toUpperCase()) != null) { + Enchantment enchantment = Enchantment.getByName(option.replace(" ", "_").toUpperCase()); + if (item.getType() != Material.ENCHANTED_BOOK) { + meta.addEnchant(enchantment, Integer.parseInt(value), true); + } else { + ((EnchantmentStorageMeta) meta).addStoredEnchant(enchantment, Integer.parseInt(value), true); + } + } + + String effect = ""; + int duration = 0; + int hit = 0; + + value = value.replace("_", " "); + switch (option) { + case "title": + if (item.getType() == Material.WRITTEN_BOOK) { + ((BookMeta) meta).setTitle(value); + } else meta.setDisplayName(value); + break; + case "lore": + String[] parts = value.split("\\|"); + ArrayList lore = new ArrayList<>(); + for (String line : parts) + lore.add(Arconix.pl().getApi().format().formatText(line)); + meta.setLore(lore); + break; + case "player": + if (item.getType() == Material.PLAYER_HEAD) { + if (value.length() == 36) + ((SkullMeta) meta).setOwningPlayer(Bukkit.getOfflinePlayer(UUID.fromString(value))); + else + ((SkullMeta) meta).setOwner(value); + } + break; + case "author": + if (item.getType() == Material.WRITTEN_BOOK) { + ((BookMeta) meta).setAuthor(value); + } + break; + case "effect": + case "duration": + hit++; + if (option.equalsIgnoreCase("effect")) { + effect = value; + } else { + duration = Integer.parseInt(value); + } + + if (hit == 2) { + PotionEffect effect2 = PotionEffectType.getByName(effect).createEffect(duration, 0); + ((PotionMeta) meta).addCustomEffect(effect2, false); + } + + break; + case "id": + if (item.getType() == Material.WRITTEN_BOOK) { + if (!UltimateKits.getInstance().getDataFile().getConfig().contains("Books.pages." + value)) + continue; + ConfigurationSection cs = UltimateKits.getInstance().getDataFile().getConfig().getConfigurationSection("Books.pages." + value); + for (String key : cs.getKeys(false)) { + ((BookMeta) meta).addPage(UltimateKits.getInstance().getDataFile().getConfig().getString("Books.pages." + value + "." + key)); + } + } + break; + case "color": + switch (item.getType()) { + case POTION: + //ToDO: this + break; + case LEATHER_HELMET: + case LEATHER_CHESTPLATE: + case LEATHER_LEGGINGS: + case LEATHER_BOOTS: + ((LeatherArmorMeta) meta).setColor(Color.fromRGB(Integer.parseInt(value))); + break; + } + break; + } + } + } + item.setItemMeta(meta); + return item; + } + + private String fixLine(String line) { + line = line.replace(" ", "_"); + return line; + } + + private String unfixLine(String line) { + line = line.replace("_", " "); + return line; + } +} diff --git a/src/main/java/com/songoda/ultimatekits/utils/Methods.java b/src/main/java/com/songoda/ultimatekits/utils/Methods.java index e5302ff..8f6341b 100644 --- a/src/main/java/com/songoda/ultimatekits/utils/Methods.java +++ b/src/main/java/com/songoda/ultimatekits/utils/Methods.java @@ -77,210 +77,6 @@ public class Methods { return true; } - /** - * Inserts the version declaration for any string containing NMS - * - * @param s the string to format, must contain NMS. - * @return formatted string - */ - public static String formatNMS(String s) { - String packageName = Bukkit.getServer().getClass().getPackage().getName(); - String nmsVersion = packageName.substring(packageName.lastIndexOf('.') + 1); - return s.replace("NMS", nmsVersion); - } - - /** - * Deserializes a JSON String - * - * @param jsonString the JSON String to parse - * @return the deserialized ItemStack - */ - public static ItemStack deserializeItemStackFromJson(String jsonString) { - try { - Method parseString = Class.forName(formatNMS("net.minecraft.server.NMS.MojangsonParser")).getMethod("parse", String.class); - Object nbtTagCompound = parseString.invoke(null, jsonString); - - Method toItemStack = Class.forName(formatNMS("net.minecraft.server.NMS.ItemStack")).getMethod("a", nbtTagCompound.getClass()); - Object citemStack = toItemStack.invoke(null, nbtTagCompound); - - Method tobItemStack = Class.forName(formatNMS("org.bukkit.craftbukkit.NMS.inventory.CraftItemStack")).getMethod("asBukkitCopy", citemStack.getClass()); - return (ItemStack) tobItemStack.invoke(null, citemStack); - - } catch (Exception ex) { - ex.printStackTrace(); - return null; - } - } - - /** - * Serializes an item stack - * - * @param itemStack the ItemStack to parse - * @return condensed JSON String - */ - public static String serializeItemStackToJson(ItemStack itemStack) { - try { - Method tocItemStack = Class.forName(formatNMS("org.bukkit.craftbukkit.NMS.inventory.CraftItemStack")).getDeclaredMethod("asNMSCopy", Class.forName("org.bukkit.inventory.ItemStack")); - Object citemStack = tocItemStack.invoke(null, itemStack); - - Object nbtTagCompoundObject = Class.forName(formatNMS("net.minecraft.server.NMS.NBTTagCompound")).newInstance(); - Method saveTagToStack = citemStack.getClass().getMethod("save", nbtTagCompoundObject.getClass()); - saveTagToStack.invoke(citemStack, nbtTagCompoundObject); - - return (String) nbtTagCompoundObject.getClass().getMethod("toString").invoke(nbtTagCompoundObject); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - - } - - - /** - * Deserializes a string to an item stack, support both formats - * - * @param string the formatted string - * @return the deserialized ItemStack - */ - public static ItemStack deserializeLegacyItemStack(String string) { - if(string.contains("{")) { - // string is json - return deserializeItemStackFromJson(string); - } - // old format - return deserializeItemStack(string); - } - - /** - * This method is not able to handle skulls or in general nbt tags. - * Method is still existing for converting purposes. - * - * @deprecated use {@link #serializeItemStackToJson(ItemStack is)} instead. - */ - @Deprecated - public static ItemStack deserializeItemStack(String string) { - string = string.replace("&", "§"); - String[] splited = string.split("\\s+"); - - String[] val = splited[0].split(":"); - ItemStack item = new ItemStack(Material.valueOf(val[0])); - - if (item.getType() == Material.PLAYER_HEAD) { - item = new ItemStack(Material.PLAYER_HEAD, 1, (byte) 3); - } - - ItemMeta meta = item.getItemMeta(); - - if (val.length == 2) { - item.setDurability(Short.parseShort(val[1])); - } - if (splited.length >= 2) { - if (Arconix.pl().getApi().doMath().isNumeric(splited[1])) { - item.setAmount(Integer.parseInt(splited[1])); - } - - for (String st : splited) { - String str = unfixLine(st); - if (!str.contains(":")) continue; - String[] ops = str.split(":", 2); - - String option = ops[0]; - String value = ops[1]; - - if (Enchantment.getByName(option.replace(" ", "_").toUpperCase()) != null) { - Enchantment enchantment = Enchantment.getByName(option.replace(" ", "_").toUpperCase()); - if (item.getType() != Material.ENCHANTED_BOOK) { - meta.addEnchant(enchantment, Integer.parseInt(value), true); - } else { - ((EnchantmentStorageMeta) meta).addStoredEnchant(enchantment, Integer.parseInt(value), true); - } - } - - String effect = ""; - int duration = 0; - int hit = 0; - - value = value.replace("_", " "); - switch (option) { - case "title": - if (item.getType() == Material.WRITTEN_BOOK) { - ((BookMeta) meta).setTitle(value); - } else meta.setDisplayName(value); - break; - case "lore": - String[] parts = value.split("\\|"); - ArrayList lore = new ArrayList<>(); - for (String line : parts) - lore.add(Arconix.pl().getApi().format().formatText(line)); - meta.setLore(lore); - break; - case "player": - if (item.getType() == Material.PLAYER_HEAD) { - if (value.length() == 36) - ((SkullMeta) meta).setOwningPlayer(Bukkit.getOfflinePlayer(UUID.fromString(value))); - else - ((SkullMeta) meta).setOwner(value); - } - break; - case "author": - if (item.getType() == Material.WRITTEN_BOOK) { - ((BookMeta) meta).setAuthor(value); - } - break; - case "effect": - case "duration": - hit++; - if (option.equalsIgnoreCase("effect")) { - effect = value; - } else { - duration = Integer.parseInt(value); - } - - if (hit == 2) { - PotionEffect effect2 = PotionEffectType.getByName(effect).createEffect(duration, 0); - ((PotionMeta) meta).addCustomEffect(effect2, false); - } - - break; - case "id": - if (item.getType() == Material.WRITTEN_BOOK) { - if (!UltimateKits.getInstance().getDataFile().getConfig().contains("Books.pages." + value)) - continue; - ConfigurationSection cs = UltimateKits.getInstance().getDataFile().getConfig().getConfigurationSection("Books.pages." + value); - for (String key : cs.getKeys(false)) { - ((BookMeta) meta).addPage(UltimateKits.getInstance().getDataFile().getConfig().getString("Books.pages." + value + "." + key)); - } - } - break; - case "color": - switch (item.getType()) { - case POTION: - //ToDO: this - break; - case LEATHER_HELMET: - case LEATHER_CHESTPLATE: - case LEATHER_LEGGINGS: - case LEATHER_BOOTS: - ((LeatherArmorMeta) meta).setColor(Color.fromRGB(Integer.parseInt(value))); - break; - } - break; - } - } - } - item.setItemMeta(meta); - return item; - } - - public static String fixLine(String line) { - line = line.replace(" ", "_"); - return line; - } - - public static String unfixLine(String line) { - line = line.replace("_", " "); - return line; - } public static String getKitFromLocation(Location location) { return UltimateKits.getInstance().getConfig().getString("data.block." + Arconix.pl().getApi().serialize().serializeLocation(location));