#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
This commit is contained in:
Mystiflow 2020-07-09 19:14:01 +10:00 committed by md_5
parent c5610a6a13
commit fd4864d475
No known key found for this signature in database
GPG Key ID: E8E901AC7C617C11
9 changed files with 100 additions and 31 deletions

View File

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

View File

@ -30,7 +30,9 @@ public class ItemSerializer implements JsonSerializer<Item>, JsonDeserializer<It
} else if ( countObj.isString() )
{
String cString = countObj.getAsString();
if ( cString.endsWith( "b" ) )
char last = cString.charAt( cString.length() - 1 );
// Check for all number suffixes
if ( last == 'b' || last == 's' || last == 'l' || last == 'f' || last == 'd' )
{
cString = cString.substring( 0, cString.length() - 1 );
}

View File

@ -18,9 +18,9 @@ public class TextSerializer implements JsonSerializer<Text>, JsonDeserializer<Te
if ( element.isJsonArray() )
{
return new Text( context.<BaseComponent[]>deserialize( 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[]

View File

@ -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,8 +79,27 @@ public class BaseComponentSerializer
{
continue;
}
Content[] list;
JsonElement contents = event.get( type );
try
{
// 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 ) );
@ -90,8 +110,8 @@ public class BaseComponentSerializer
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;

View File

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

View File

@ -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" ) )
{

View File

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

View File

@ -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" ) )
{

View File

@ -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<BaseComponent> list = new ArrayList<BaseComponent>( Arrays.asList( builderComponents ) );
Assert.assertEquals(
TextComponent.toLegacyText( components ),
TextComponent.toLegacyText( list.toArray( new BaseComponent[ list.size() ] ) )
);
Assert.assertArrayEquals( components, builderComponents );
}
/*