From fd4864d475f705b348adec3fe8f48754b5ee11e6 Mon Sep 17 00:00:00 2001 From: Mystiflow Date: Thu, 9 Jul 2020 19:14:01 +1000 Subject: [PATCH] #2899: Various chat API stability updates - Check if a value CAN be parsed as a BaseComponent[] before attempting to parse it through the Content deserialiser - When removing enclosing quotes from deserialised NBT, don't remove all quotes as they may have been escaping - Check for ALL the number suffix types - Throw JSONParseException if: no selector in selector component, no translate in translate component - JsonObject is not JsonPrimitive - Cleaned up unit tests a bit --- .../net/md_5/bungee/api/chat/ItemTag.java | 9 +++- .../chat/hover/content/ItemSerializer.java | 4 +- .../chat/hover/content/TextSerializer.java | 4 +- .../bungee/chat/BaseComponentSerializer.java | 42 +++++++++++---- .../chat/KeybindComponentSerializer.java | 6 ++- .../bungee/chat/ScoreComponentSerializer.java | 4 ++ .../chat/SelectorComponentSerializer.java | 4 ++ .../chat/TranslatableComponentSerializer.java | 4 ++ .../md_5/bungee/api/chat/ComponentsTest.java | 54 +++++++++++++------ 9 files changed, 100 insertions(+), 31 deletions(-) diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/ItemTag.java b/chat/src/main/java/net/md_5/bungee/api/chat/ItemTag.java index d05e5d8b..d49897c7 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/ItemTag.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/ItemTag.java @@ -63,7 +63,14 @@ public final class ItemTag @Override public ItemTag deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException { - return ofNbt( element.toString().replace( "\"", "" ) ); + // Remove the enclosing string quotes. + String eString = element.toString(); + if ( eString.length() >= 2 && eString.charAt( 0 ) == '\"' && eString.charAt( eString.length() - 1 ) == '\"' ) + { + eString = eString.substring( 1, eString.length() - 1 ); + } + + return ItemTag.ofNbt( eString ); } @Override diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/ItemSerializer.java b/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/ItemSerializer.java index 0ba8f0b9..13af960c 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/ItemSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/hover/content/ItemSerializer.java @@ -30,7 +30,9 @@ public class ItemSerializer implements JsonSerializer, JsonDeserializer, JsonDeserializerdeserialize( element, BaseComponent[].class ) ); - } else if ( element.getAsJsonObject().isJsonPrimitive() ) + } else if ( element.isJsonPrimitive() ) { - return new Text( element.getAsJsonObject().getAsJsonPrimitive().getAsString() ); + return new Text( element.getAsJsonPrimitive().getAsString() ); } else { return new Text( new BaseComponent[] diff --git a/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java index 4387841b..83ee608e 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -4,6 +4,7 @@ import com.google.common.base.Preconditions; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import java.util.ArrayList; import java.util.Arrays; @@ -78,20 +79,39 @@ public class BaseComponentSerializer { continue; } - Content[] list; JsonElement contents = event.get( type ); - if ( contents.isJsonArray() ) + try { - list = context.deserialize( contents, HoverEvent.getClass( action, true ) ); - } else - { - list = new Content[] - { - context.deserialize( contents, HoverEvent.getClass( action, false ) ) - }; - } - hoverEvent = new HoverEvent( action, new ArrayList<>( Arrays.asList( list ) ) ); + // Plugins previously had support to pass BaseComponent[] into any action. + // If the GSON is possible to be parsed as BaseComponent, attempt to parse as so. + BaseComponent[] components; + if ( contents.isJsonArray() ) + { + components = context.deserialize( contents, BaseComponent[].class ); + } else + { + components = new BaseComponent[] + { + context.deserialize( contents, BaseComponent.class ) + }; + } + hoverEvent = new HoverEvent( action, components ); + } catch ( JsonParseException ex ) + { + Content[] list; + if ( contents.isJsonArray() ) + { + list = context.deserialize( contents, HoverEvent.getClass( action, true ) ); + } else + { + list = new Content[] + { + context.deserialize( contents, HoverEvent.getClass( action, false ) ) + }; + } + hoverEvent = new HoverEvent( action, new ArrayList<>( Arrays.asList( list ) ) ); + } // stop the loop as soon as either one is found break; diff --git a/chat/src/main/java/net/md_5/bungee/chat/KeybindComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/KeybindComponentSerializer.java index a86a40a6..f9cfa20e 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/KeybindComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/KeybindComponentSerializer.java @@ -16,8 +16,12 @@ public class KeybindComponentSerializer extends BaseComponentSerializer implemen @Override public KeybindComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - KeybindComponent component = new KeybindComponent(); JsonObject object = json.getAsJsonObject(); + if ( !object.has( "keybind" ) ) + { + throw new JsonParseException( "Could not parse JSON: missing 'keybind' property" ); + } + KeybindComponent component = new KeybindComponent(); deserialize( object, component, context ); component.setKeybind( object.get( "keybind" ).getAsString() ); return component; diff --git a/chat/src/main/java/net/md_5/bungee/chat/ScoreComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/ScoreComponentSerializer.java index f09c8bf9..e38bf94e 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/ScoreComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/ScoreComponentSerializer.java @@ -17,6 +17,10 @@ public class ScoreComponentSerializer extends BaseComponentSerializer implements public ScoreComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException { JsonObject json = element.getAsJsonObject(); + if ( !json.has( "score" ) ) + { + throw new JsonParseException( "Could not parse JSON: missing 'score' property" ); + } JsonObject score = json.get( "score" ).getAsJsonObject(); if ( !score.has( "name" ) || !score.has( "objective" ) ) { diff --git a/chat/src/main/java/net/md_5/bungee/chat/SelectorComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/SelectorComponentSerializer.java index 34fd2f69..0176a298 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/SelectorComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/SelectorComponentSerializer.java @@ -17,6 +17,10 @@ public class SelectorComponentSerializer extends BaseComponentSerializer impleme public SelectorComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException { JsonObject object = element.getAsJsonObject(); + if ( !object.has( "selector" ) ) + { + throw new JsonParseException( "Could not parse JSON: missing 'selector' property" ); + } SelectorComponent component = new SelectorComponent( object.get( "selector" ).getAsString() ); deserialize( object, component, context ); return component; diff --git a/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java index c6057e3a..2ab65ffb 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java @@ -21,6 +21,10 @@ public class TranslatableComponentSerializer extends BaseComponentSerializer imp TranslatableComponent component = new TranslatableComponent(); JsonObject object = json.getAsJsonObject(); deserialize( object, component, context ); + if ( !object.has( "translate" ) ) + { + throw new JsonParseException( "Could not parse JSON: missing 'translate' property" ); + } component.setTranslate( object.get( "translate" ).getAsString() ); if ( object.has( "with" ) ) { diff --git a/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java b/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java index 3c14d275..3922fbea 100644 --- a/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java +++ b/chat/src/test/java/net/md_5/bungee/api/chat/ComponentsTest.java @@ -1,9 +1,6 @@ package net.md_5.bungee.api.chat; import java.awt.Color; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.hover.content.Item; import net.md_5.bungee.api.chat.hover.content.Text; @@ -14,14 +11,45 @@ import org.junit.Test; public class ComponentsTest { + public static void testDissembleReassemble(BaseComponent[] components) + { + String json = ComponentSerializer.toString( components ); + BaseComponent[] parsed = ComponentSerializer.parse( json ); + Assert.assertEquals( TextComponent.toLegacyText( parsed ), TextComponent.toLegacyText( components ) ); + } + + public static void testDissembleReassemble(String json) + { + BaseComponent[] parsed = ComponentSerializer.parse( json ); + Assert.assertEquals( json, ComponentSerializer.toString( parsed ) ); + } + @Test public void testItemParse() { - String json = "{\"extra\":[{\"text\":\"[\"},{\"extra\":[{\"translate\":\"block.minecraft.dirt\"}],\"text\":\"\"},{\"text\":\"]\"}],\"hoverEvent\":{\"action\":\"show_item\",\"value\":[{\"text\":\"{id:\\\"minecraft:dirt\\\",Count:1b}\"}]},\"text\":\"\"}"; - BaseComponent[] component = ComponentSerializer.parse( json ); - String serialised = ComponentSerializer.toString( component ); - BaseComponent[] deserialised = ComponentSerializer.parse( serialised ); - Assert.assertEquals( TextComponent.toLegacyText( deserialised ), TextComponent.toLegacyText( component ) ); + // Declare all commonly used variables for reuse. + BaseComponent[] components; + TextComponent textComponent; + String json; + + textComponent = new TextComponent( "Test" ); + textComponent.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_ITEM, new BaseComponent[] + { + new TextComponent( "{id:\"minecraft:netherrack\",Count:47b}" ) + } ) ); + testDissembleReassemble( new BaseComponent[] + { + textComponent + } ); + json = "{\"text\":\"Test\",\"hoverEvent\":{\"action\":\"show_item\",\"value\":[{\"text\":\"{id:\\\"minecraft:netherrack\\\",Count:47b}\"}]}}"; + testDissembleReassemble( json ); + ////////// + String hoverVal = "{\"text\":\"{id:\\\"minecraft:dirt\\\",Count:1b}\"}"; + json = "{\"extra\":[{\"text\":\"[\"},{\"extra\":[{\"translate\":\"block.minecraft.dirt\"}],\"text\":\"\"},{\"text\":\"]\"}],\"hoverEvent\":{\"action\":\"show_item\",\"value\":[" + hoverVal + "]},\"text\":\"\"}"; + components = ComponentSerializer.parse( json ); + Text contentText = ( (Text) components[0].getHoverEvent().getContents().get( 0 ) ); + Assert.assertEquals( hoverVal, ComponentSerializer.toString( (BaseComponent[]) contentText.getValue() ) ); + testDissembleReassemble( components ); ////////// TextComponent component1 = new TextComponent( "HoverableText" ); String nbt = "{display:{Name:{text:Hello},Lore:[{text:Line_1},{text:Line_2}]},ench:[{id:49,lvl:5}],Unbreakable:1}}"; @@ -29,8 +57,8 @@ public class ComponentsTest HoverEvent hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_ITEM, contentItem ); component1.setHoverEvent( hoverEvent ); json = ComponentSerializer.toString( component1 ); - component = ComponentSerializer.parse( json ); - Item parsedContentItem = ( (Item) component[0].getHoverEvent().getContents().get( 0 ) ); + components = ComponentSerializer.parse( json ); + Item parsedContentItem = ( (Item) components[0].getHoverEvent().getContents().get( 0 ) ); Assert.assertEquals( contentItem, parsedContentItem ); Assert.assertEquals( contentItem.getCount(), parsedContentItem.getCount() ); Assert.assertEquals( contentItem.getId(), parsedContentItem.getId() ); @@ -142,11 +170,7 @@ public class ComponentsTest String text = "§a§lHello §r§kworld§7!"; BaseComponent[] components = TextComponent.fromLegacyText( text ); BaseComponent[] builderComponents = new ComponentBuilder().append( components ).create(); - List list = new ArrayList( Arrays.asList( builderComponents ) ); - Assert.assertEquals( - TextComponent.toLegacyText( components ), - TextComponent.toLegacyText( list.toArray( new BaseComponent[ list.size() ] ) ) - ); + Assert.assertArrayEquals( components, builderComponents ); } /*