From e42d37e86d7bad09003217f0fc2f08fb1c6029ac Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Tue, 19 Jan 2021 07:30:09 -0500 Subject: [PATCH] Fix various problems with firework meta (de)serialization (#3905) Adds support for parsing fireworks charges (would previously cause exceptions due to illegal casts to FireworkMeta) and fixes createkit from producing invalid color values Thanks to triagonal for reporting the exception caused by createkit. Fixes #1283. --- .../earth2me/essentials/MetaItemStack.java | 82 ++++++++++++++++++- .../essentials/items/AbstractItemDb.java | 62 ++++++++------ .../essentials/utils/MaterialUtil.java | 10 ++- 3 files changed, 125 insertions(+), 29 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java b/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java index 7280d322e..40292ba23 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java +++ b/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java @@ -23,6 +23,7 @@ import org.bukkit.inventory.meta.BannerMeta; import org.bukkit.inventory.meta.BlockStateMeta; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.bukkit.inventory.meta.FireworkEffectMeta; import org.bukkit.inventory.meta.FireworkMeta; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.LeatherArmorMeta; @@ -61,12 +62,14 @@ public class MetaItemStack { } } - private final transient Pattern splitPattern = Pattern.compile("[:+',;.]"); + private static final transient Pattern splitPattern = Pattern.compile("[:+',;.]"); + private static final transient Pattern hexPattern = Pattern.compile("#([0-9a-fA-F]{6})"); private ItemStack stack; private FireworkEffect.Builder builder = FireworkEffect.builder(); private PotionEffectType pEffectType; private PotionEffect pEffect; private boolean validFirework = false; + private boolean validFireworkCharge = false; private boolean validPotionEffect = false; private boolean validPotionDuration = false; private boolean validPotionPower = false; @@ -174,6 +177,15 @@ public class MetaItemStack { } stack.setItemMeta(fmeta); } + if (validFireworkCharge) { + if (!hasMetaPermission(sender, "firework", true, true, ess)) { + throw new Exception(tl("noMetaFirework")); + } + final FireworkEffect effect = builder.build(); + final FireworkEffectMeta meta = (FireworkEffectMeta) stack.getItemMeta(); + meta.setEffect(effect); + stack.setItemMeta(meta); + } } } @@ -236,6 +248,8 @@ public class MetaItemStack { } else if (MaterialUtil.isFirework(stack.getType())) { //WARNING - Meta for fireworks will be ignored after this point. addFireworkMeta(sender, false, string, ess); + } else if (MaterialUtil.isFireworkCharge(stack.getType())) { + addChargeMeta(sender, false, string, ess); } else if (MaterialUtil.isPotion(stack.getType())) { //WARNING - Meta for potions will be ignored after this point. addPotionMeta(sender, false, string, ess); @@ -300,6 +314,67 @@ public class MetaItemStack { stack.setItemMeta(meta); } + private void addChargeMeta(final CommandSource sender, final boolean allowShortName, final String string, final IEssentials ess) throws Exception { + final String[] split = splitPattern.split(string, 2); + if (split.length < 2) { + return; + } + + if (split[0].equalsIgnoreCase("color") || split[0].equalsIgnoreCase("colour") || (allowShortName && split[0].equalsIgnoreCase("c"))) { + final List primaryColors = new ArrayList<>(); + final String[] colors = split[1].split(","); + for (final String color : colors) { + if (colorMap.containsKey(color.toUpperCase())) { + validFireworkCharge = true; + primaryColors.add(colorMap.get(color.toUpperCase()).getFireworkColor()); + } else if (hexPattern.matcher(color).matches()) { + validFireworkCharge = true; + primaryColors.add(Color.fromRGB(Integer.decode(color))); + } else { + throw new Exception(tl("invalidFireworkFormat", split[1], split[0])); + } + } + builder.withColor(primaryColors); + } else if (split[0].equalsIgnoreCase("shape") || split[0].equalsIgnoreCase("type") || (allowShortName && (split[0].equalsIgnoreCase("s") || split[0].equalsIgnoreCase("t")))) { + FireworkEffect.Type finalEffect = null; + split[1] = split[1].equalsIgnoreCase("large") ? "BALL_LARGE" : split[1]; + if (fireworkShape.containsKey(split[1].toUpperCase())) { + finalEffect = fireworkShape.get(split[1].toUpperCase()); + } else { + throw new Exception(tl("invalidFireworkFormat", split[1], split[0])); + } + if (finalEffect != null) { + builder.with(finalEffect); + } + } else if (split[0].equalsIgnoreCase("fade") || (allowShortName && split[0].equalsIgnoreCase("f"))) { + final List fadeColors = new ArrayList<>(); + final String[] colors = split[1].split(","); + for (final String color : colors) { + if (colorMap.containsKey(color.toUpperCase())) { + fadeColors.add(colorMap.get(color.toUpperCase()).getFireworkColor()); + } else if (hexPattern.matcher(color).matches()) { + fadeColors.add(Color.fromRGB(Integer.decode(color))); + } else { + throw new Exception(tl("invalidFireworkFormat", split[1], split[0])); + } + } + if (!fadeColors.isEmpty()) { + builder.withFade(fadeColors); + } + } else if (split[0].equalsIgnoreCase("effect") || (allowShortName && split[0].equalsIgnoreCase("e"))) { + final String[] effects = split[1].split(","); + for (final String effect : effects) { + if (effect.equalsIgnoreCase("twinkle")) { + builder.flicker(true); + } else if (effect.equalsIgnoreCase("trail")) { + builder.trail(true); + } else { + throw new Exception(tl("invalidFireworkFormat", split[1], split[0])); + } + } + } + } + public void addFireworkMeta(final CommandSource sender, final boolean allowShortName, final String string, final IEssentials ess) throws Exception { if (MaterialUtil.isFirework(stack.getType())) { final String[] split = splitPattern.split(string, 2); @@ -328,6 +403,9 @@ public class MetaItemStack { if (colorMap.containsKey(color.toUpperCase())) { validFirework = true; primaryColors.add(colorMap.get(color.toUpperCase()).getFireworkColor()); + } else if (hexPattern.matcher(color).matches()) { + validFirework = true; + primaryColors.add(Color.fromRGB(Integer.decode(color))); } else { throw new Exception(tl("invalidFireworkFormat", split[1], split[0])); } @@ -350,6 +428,8 @@ public class MetaItemStack { for (final String color : colors) { if (colorMap.containsKey(color.toUpperCase())) { fadeColors.add(colorMap.get(color.toUpperCase()).getFireworkColor()); + } else if (hexPattern.matcher(color).matches()) { + fadeColors.add(Color.fromRGB(Integer.decode(color))); } else { throw new Exception(tl("invalidFireworkFormat", split[1], split[0])); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/items/AbstractItemDb.java b/Essentials/src/main/java/com/earth2me/essentials/items/AbstractItemDb.java index e219bf3b1..c5e2339bd 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/items/AbstractItemDb.java +++ b/Essentials/src/main/java/com/earth2me/essentials/items/AbstractItemDb.java @@ -18,6 +18,7 @@ import org.bukkit.inventory.meta.BannerMeta; import org.bukkit.inventory.meta.BlockStateMeta; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.bukkit.inventory.meta.FireworkEffectMeta; import org.bukkit.inventory.meta.FireworkMeta; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.LeatherArmorMeta; @@ -268,35 +269,15 @@ public abstract class AbstractItemDb implements IConf, net.ess3.api.IItemDb { final FireworkMeta fireworkMeta = (FireworkMeta) is.getItemMeta(); if (fireworkMeta.hasEffects()) { for (final FireworkEffect effect : fireworkMeta.getEffects()) { - if (effect.getColors() != null && !effect.getColors().isEmpty()) { - sb.append("color:"); - boolean first = true; - for (final Color c : effect.getColors()) { - if (!first) { - sb.append(","); // same thing as above. - } - sb.append(c.toString()); - first = false; - } - sb.append(" "); - } - - sb.append("shape:").append(effect.getType().name()).append(" "); - if (effect.getFadeColors() != null && !effect.getFadeColors().isEmpty()) { - sb.append("fade:"); - boolean first = true; - for (final Color c : effect.getFadeColors()) { - if (!first) { - sb.append(","); // same thing as above. - } - sb.append(c.toString()); - first = false; - } - sb.append(" "); - } + serializeEffectMeta(sb, effect); } sb.append("power:").append(fireworkMeta.getPower()).append(" "); } + } else if (MaterialUtil.isFireworkCharge(material)) { + final FireworkEffectMeta fireworkEffectMeta = (FireworkEffectMeta) is.getItemMeta(); + if (fireworkEffectMeta.hasEffect()) { + serializeEffectMeta(sb, fireworkEffectMeta.getEffect()); + } } else if (MaterialUtil.isPotion(material)) { final Potion potion = Potion.fromItemStack(is); for (final PotionEffect e : potion.getEffects()) { @@ -353,6 +334,35 @@ public abstract class AbstractItemDb implements IConf, net.ess3.api.IItemDb { return sb.toString().trim().replaceAll("ยง", "&"); } + private void serializeEffectMeta(StringBuilder sb, FireworkEffect effect) { + if (effect.getColors() != null && !effect.getColors().isEmpty()) { + sb.append("color:"); + boolean first = true; + for (final Color c : effect.getColors()) { + if (!first) { + sb.append(","); // same thing as above. + } + sb.append("#").append(Integer.toHexString(c.asRGB())); + first = false; + } + sb.append(" "); + } + + sb.append("shape:").append(effect.getType().name()).append(" "); + if (effect.getFadeColors() != null && !effect.getFadeColors().isEmpty()) { + sb.append("fade:"); + boolean first = true; + for (final Color c : effect.getFadeColors()) { + if (!first) { + sb.append(","); // same thing as above. + } + sb.append("#").append(Integer.toHexString(c.asRGB())); + first = false; + } + sb.append(" "); + } + } + @Override public boolean isReady() { return ready; diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/MaterialUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/MaterialUtil.java index a2e82af9c..2a4e4c3e2 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/MaterialUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/MaterialUtil.java @@ -16,6 +16,7 @@ public final class MaterialUtil { private static final Set BEDS; private static final Set BANNERS; private static final Set FIREWORKS; + private static final Set FIREWORK_CHARGE; private static final Set LEGACY_SKULLS; private static final Set LEATHER_ARMOR; private static final Set MOB_HEADS; @@ -54,8 +55,9 @@ public final class MaterialUtil { "PINK_BANNER", "GRAY_BANNER", "LIGHT_GRAY_BANNER", "CYAN_BANNER", "PURPLE_BANNER", "BLUE_BANNER", "BROWN_BANNER", "GREEN_BANNER", "RED_BANNER", "BLACK_BANNER", "SHIELD"); - FIREWORKS = EnumUtil.getAllMatching(Material.class, "FIREWORK", "FIREWORK_ROCKET", - "FIREWORK_CHARGE", "FIREWORK_STAR"); + FIREWORKS = EnumUtil.getAllMatching(Material.class, "FIREWORK", "FIREWORK_ROCKET"); + + FIREWORK_CHARGE = EnumUtil.getAllMatching(Material.class, "FIREWORK_CHARGE", "FIREWORK_STAR"); LEATHER_ARMOR = EnumUtil.getAllMatching(Material.class, "LEATHER_HELMET", "LEATHER_CHESTPLATE", "LEATHER_LEGGINGS", "LEATHER_BOOTS"); @@ -116,6 +118,10 @@ public final class MaterialUtil { return FIREWORKS.contains(material); } + public static boolean isFireworkCharge(final Material material) { + return FIREWORK_CHARGE.contains(material); + } + public static boolean isLeatherArmor(final Material material) { return LEATHER_ARMOR.contains(material); }