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.
This commit is contained in:
Josh Roy 2021-01-19 07:30:09 -05:00 committed by GitHub
parent 191c9e9302
commit e42d37e86d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 125 additions and 29 deletions

View File

@ -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<Color> 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<Color> 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]));
}

View File

@ -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;

View File

@ -16,6 +16,7 @@ public final class MaterialUtil {
private static final Set<Material> BEDS;
private static final Set<Material> BANNERS;
private static final Set<Material> FIREWORKS;
private static final Set<Material> FIREWORK_CHARGE;
private static final Set<Material> LEGACY_SKULLS;
private static final Set<Material> LEATHER_ARMOR;
private static final Set<Material> 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);
}