Minecraft 25w20a protocol support
This commit is contained in:
31
serializer/nb-configuration.xml
Normal file
31
serializer/nb-configuration.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project-shared-configuration>
|
||||
<!--
|
||||
This file contains additional configuration written by modules in the NetBeans IDE.
|
||||
The configuration is intended to be shared among all the users of project and
|
||||
therefore it is assumed to be part of version control checkout.
|
||||
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
|
||||
-->
|
||||
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
|
||||
<!--
|
||||
Properties that influence various parts of the IDE, especially code formatting and the like.
|
||||
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
|
||||
That way multiple projects can share the same settings (useful for formatting rules for example).
|
||||
Any value defined here will override the pom.xml file value but is only applicable to the current project.
|
||||
-->
|
||||
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
|
||||
</properties>
|
||||
</project-shared-configuration>
|
41
serializer/pom.xml
Normal file
41
serializer/pom.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.21-R0.3-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-serializer</artifactId>
|
||||
<version>1.21-R0.3-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Serializer</name>
|
||||
<description>Minecraft JSON serializer intended for use with BungeeCord</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.11.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>bungeecord-chat</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>bungeecord-dialog</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@@ -0,0 +1,78 @@
|
||||
package net.md_5.bungee.api.chat.hover.content;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.UUID;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.chat.BaseComponentSerializer;
|
||||
import net.md_5.bungee.chat.VersionedComponentSerializer;
|
||||
|
||||
public class EntitySerializer extends BaseComponentSerializer implements JsonSerializer<Entity>, JsonDeserializer<Entity>
|
||||
{
|
||||
|
||||
public EntitySerializer(VersionedComponentSerializer serializer)
|
||||
{
|
||||
super( serializer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
JsonObject value = element.getAsJsonObject();
|
||||
|
||||
boolean newEntity = value.has( "uuid" );
|
||||
|
||||
String idString;
|
||||
JsonElement uuid = value.get( newEntity ? "uuid" : "id" );
|
||||
if ( uuid.isJsonArray() )
|
||||
{
|
||||
idString = parseUUID( context.deserialize( uuid, int[].class ) ).toString();
|
||||
} else
|
||||
{
|
||||
idString = uuid.getAsString();
|
||||
}
|
||||
|
||||
return new Entity(
|
||||
( value.has( newEntity ? "id" : "type" ) ) ? value.get( newEntity ? "id" : "type" ).getAsString() : null,
|
||||
idString,
|
||||
( value.has( "name" ) ) ? context.deserialize( value.get( "name" ), BaseComponent.class ) : null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Entity content, Type type, JsonSerializationContext context)
|
||||
{
|
||||
JsonObject object = new JsonObject();
|
||||
|
||||
switch ( serializer.getVersion() )
|
||||
{
|
||||
case V1_21_5:
|
||||
object.addProperty( "id", ( content.getType() != null ) ? content.getType() : "minecraft:pig" );
|
||||
object.addProperty( "uuid", content.getId() );
|
||||
break;
|
||||
case V1_16:
|
||||
object.addProperty( "type", ( content.getType() != null ) ? content.getType() : "minecraft:pig" );
|
||||
object.addProperty( "id", content.getId() );
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() );
|
||||
}
|
||||
|
||||
if ( content.getName() != null )
|
||||
{
|
||||
object.add( "name", context.serialize( content.getName() ) );
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
private static UUID parseUUID(int[] array)
|
||||
{
|
||||
return new UUID( (long) array[0] << 32 | (long) array[1] & 0XFFFFFFFFL, (long) array[2] << 32 | (long) array[3] & 0XFFFFFFFFL );
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
package net.md_5.bungee.api.chat.hover.content;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import net.md_5.bungee.api.chat.ItemTag;
|
||||
|
||||
public class ItemSerializer implements JsonSerializer<Item>, JsonDeserializer<Item>
|
||||
{
|
||||
|
||||
@Override
|
||||
public Item deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
JsonObject value = element.getAsJsonObject();
|
||||
|
||||
int count = -1;
|
||||
if ( value.has( "Count" ) )
|
||||
{
|
||||
JsonPrimitive countObj = value.get( "Count" ).getAsJsonPrimitive();
|
||||
|
||||
if ( countObj.isNumber() )
|
||||
{
|
||||
count = countObj.getAsInt();
|
||||
} else if ( countObj.isString() )
|
||||
{
|
||||
String cString = countObj.getAsString();
|
||||
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 );
|
||||
}
|
||||
try
|
||||
{
|
||||
count = Integer.parseInt( cString );
|
||||
} catch ( NumberFormatException ex )
|
||||
{
|
||||
throw new JsonParseException( "Could not parse count: " + ex );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Item(
|
||||
( value.has( "id" ) ) ? value.get( "id" ).getAsString() : null,
|
||||
count,
|
||||
( value.has( "tag" ) ) ? context.deserialize( value.get( "tag" ), ItemTag.class ) : null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Item content, Type type, JsonSerializationContext context)
|
||||
{
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty( "id", ( content.getId() == null ) ? "minecraft:air" : content.getId() );
|
||||
if ( content.getCount() != -1 )
|
||||
{
|
||||
object.addProperty( "Count", content.getCount() );
|
||||
}
|
||||
if ( content.getTag() != null )
|
||||
{
|
||||
object.add( "tag", context.serialize( content.getTag() ) );
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package net.md_5.bungee.api.chat.hover.content;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
public class TextSerializer implements JsonSerializer<Text>, JsonDeserializer<Text>
|
||||
{
|
||||
|
||||
@Override
|
||||
public Text deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
if ( element.isJsonArray() )
|
||||
{
|
||||
return new Text( context.<BaseComponent[]>deserialize( element, BaseComponent[].class ) );
|
||||
} else if ( element.isJsonPrimitive() )
|
||||
{
|
||||
return new Text( element.getAsJsonPrimitive().getAsString() );
|
||||
} else
|
||||
{
|
||||
return new Text( new BaseComponent[]
|
||||
{
|
||||
context.deserialize( element, BaseComponent.class )
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Text content, Type type, JsonSerializationContext context)
|
||||
{
|
||||
return context.serialize( content.getValue() );
|
||||
}
|
||||
}
|
@@ -0,0 +1,276 @@
|
||||
package net.md_5.bungee.chat;
|
||||
|
||||
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.JsonSerializationContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Locale;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.ClickEventCustom;
|
||||
import net.md_5.bungee.api.chat.ComponentStyle;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.hover.content.Content;
|
||||
import net.md_5.bungee.api.dialog.chat.ShowDialogClickEvent;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BaseComponentSerializer
|
||||
{
|
||||
|
||||
protected final VersionedComponentSerializer serializer;
|
||||
|
||||
protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context)
|
||||
{
|
||||
component.applyStyle( context.deserialize( object, ComponentStyle.class ) );
|
||||
|
||||
JsonElement insertion = object.get( "insertion" );
|
||||
if ( insertion != null )
|
||||
{
|
||||
component.setInsertion( insertion.getAsString() );
|
||||
}
|
||||
|
||||
//Events
|
||||
JsonObject clickEvent;
|
||||
boolean newClickEvent = ( clickEvent = object.getAsJsonObject( "click_event" ) ) != null;
|
||||
if ( !newClickEvent )
|
||||
{
|
||||
clickEvent = object.getAsJsonObject( "clickEvent" );
|
||||
}
|
||||
if ( clickEvent != null )
|
||||
{
|
||||
ClickEvent.Action action = ClickEvent.Action.valueOf( clickEvent.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) );
|
||||
if ( newClickEvent )
|
||||
{
|
||||
switch ( action )
|
||||
{
|
||||
case OPEN_URL:
|
||||
component.setClickEvent( new ClickEvent( action, clickEvent.get( "url" ).getAsString() ) );
|
||||
break;
|
||||
case RUN_COMMAND:
|
||||
case SUGGEST_COMMAND:
|
||||
component.setClickEvent( new ClickEvent( action, clickEvent.get( "command" ).getAsString() ) );
|
||||
break;
|
||||
case CHANGE_PAGE:
|
||||
int page = clickEvent.get( "page" ).getAsInt();
|
||||
Preconditions.checkArgument( page >= 0, "Page number has to be positive" );
|
||||
component.setClickEvent( new ClickEvent( action, Integer.toString( page ) ) );
|
||||
break;
|
||||
case SHOW_DIALOG:
|
||||
component.setClickEvent( context.deserialize( clickEvent.get( "dialog" ), ShowDialogClickEvent.class ) );
|
||||
break;
|
||||
case CUSTOM:
|
||||
component.setClickEvent( new ClickEventCustom( clickEvent.get( "id" ).getAsString(), ( clickEvent.has( "payload" ) ) ? clickEvent.get( "payload" ).getAsString() : null ) );
|
||||
break;
|
||||
default:
|
||||
component.setClickEvent( new ClickEvent( action, ( clickEvent.has( "value" ) ) ? clickEvent.get( "value" ).getAsString() : "" ) );
|
||||
break;
|
||||
}
|
||||
} else
|
||||
{
|
||||
component.setClickEvent( new ClickEvent( action, ( clickEvent.has( "value" ) ) ? clickEvent.get( "value" ).getAsString() : "" ) );
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject hoverEventJson;
|
||||
boolean newHoverEvent = ( hoverEventJson = object.getAsJsonObject( "hover_event" ) ) != null;
|
||||
if ( !newHoverEvent )
|
||||
{
|
||||
hoverEventJson = object.getAsJsonObject( "hoverEvent" );
|
||||
}
|
||||
|
||||
if ( hoverEventJson != null )
|
||||
{
|
||||
HoverEvent hoverEvent = null;
|
||||
HoverEvent.Action action = HoverEvent.Action.valueOf( hoverEventJson.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) );
|
||||
|
||||
if ( newHoverEvent || hoverEventJson.has( "contents" ) )
|
||||
{
|
||||
// value is only used for text in >= 1.21.5 (its inlined now)
|
||||
JsonElement contents = hoverEventJson.get( newHoverEvent ? "value" : "contents" );
|
||||
if ( contents != null || ( newHoverEvent && ( action == HoverEvent.Action.SHOW_ITEM || action == HoverEvent.Action.SHOW_ENTITY ) ) )
|
||||
{
|
||||
if ( contents == null )
|
||||
{
|
||||
// this is the new inline for SHOW_ITEM and SHOW_ENTITY
|
||||
contents = hoverEventJson;
|
||||
}
|
||||
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 ) ) );
|
||||
}
|
||||
} else
|
||||
{
|
||||
JsonElement value = hoverEventJson.get( "value" );
|
||||
if ( value != null )
|
||||
{
|
||||
// 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 ( value.isJsonArray() )
|
||||
{
|
||||
components = context.deserialize( value, BaseComponent[].class );
|
||||
} else
|
||||
{
|
||||
components = new BaseComponent[]
|
||||
{
|
||||
context.deserialize( value, BaseComponent.class )
|
||||
};
|
||||
}
|
||||
hoverEvent = new HoverEvent( action, components );
|
||||
}
|
||||
}
|
||||
|
||||
if ( hoverEvent != null )
|
||||
{
|
||||
component.setHoverEvent( hoverEvent );
|
||||
}
|
||||
}
|
||||
|
||||
JsonElement extra = object.get( "extra" );
|
||||
if ( extra != null )
|
||||
{
|
||||
component.setExtra( Arrays.asList( context.deserialize( extra, BaseComponent[].class ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context)
|
||||
{
|
||||
boolean first = false;
|
||||
if ( VersionedComponentSerializer.serializedComponents.get() == null )
|
||||
{
|
||||
first = true;
|
||||
VersionedComponentSerializer.serializedComponents.set( Collections.newSetFromMap( new IdentityHashMap<BaseComponent, Boolean>() ) );
|
||||
}
|
||||
try
|
||||
{
|
||||
Preconditions.checkArgument( !VersionedComponentSerializer.serializedComponents.get().contains( component ), "Component loop" );
|
||||
VersionedComponentSerializer.serializedComponents.get().add( component );
|
||||
|
||||
ComponentStyleSerializer.serializeTo( component.getStyle(), object );
|
||||
|
||||
if ( component.getInsertion() != null )
|
||||
{
|
||||
object.addProperty( "insertion", component.getInsertion() );
|
||||
}
|
||||
|
||||
//Events
|
||||
if ( component.getClickEvent() != null )
|
||||
{
|
||||
JsonObject clickEvent = new JsonObject();
|
||||
String actionName = component.getClickEvent().getAction().toString().toLowerCase( Locale.ROOT );
|
||||
clickEvent.addProperty( "action", actionName.toLowerCase( Locale.ROOT ) );
|
||||
switch ( serializer.getVersion() )
|
||||
{
|
||||
case V1_21_5:
|
||||
ClickEvent.Action action = ClickEvent.Action.valueOf( actionName.toUpperCase( Locale.ROOT ) );
|
||||
switch ( action )
|
||||
{
|
||||
case OPEN_URL:
|
||||
clickEvent.addProperty( "url", component.getClickEvent().getValue() );
|
||||
break;
|
||||
case RUN_COMMAND:
|
||||
case SUGGEST_COMMAND:
|
||||
clickEvent.addProperty( "command", component.getClickEvent().getValue() );
|
||||
break;
|
||||
case CHANGE_PAGE:
|
||||
clickEvent.addProperty( "page", Integer.parseInt( component.getClickEvent().getValue() ) );
|
||||
break;
|
||||
case SHOW_DIALOG:
|
||||
clickEvent.add( "dialog", context.serialize( component.getClickEvent() ) );
|
||||
break;
|
||||
case CUSTOM:
|
||||
ClickEventCustom custom = (ClickEventCustom) component.getClickEvent();
|
||||
clickEvent.addProperty( "id", custom.getValue() );
|
||||
if ( custom.getPayload() != null )
|
||||
{
|
||||
clickEvent.addProperty( "payload", custom.getPayload() );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
clickEvent.addProperty( "value", component.getClickEvent().getValue() );
|
||||
break;
|
||||
}
|
||||
object.add( "click_event", clickEvent );
|
||||
break;
|
||||
case V1_16:
|
||||
clickEvent.addProperty( "value", component.getClickEvent().getValue() );
|
||||
object.add( "clickEvent", clickEvent );
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() );
|
||||
}
|
||||
|
||||
}
|
||||
if ( component.getHoverEvent() != null )
|
||||
{
|
||||
JsonObject hoverEvent = new JsonObject();
|
||||
hoverEvent.addProperty( "action", component.getHoverEvent().getAction().toString().toLowerCase( Locale.ROOT ) );
|
||||
if ( component.getHoverEvent().isLegacy() )
|
||||
{
|
||||
hoverEvent.add( "value", context.serialize( component.getHoverEvent().getContents().get( 0 ) ) );
|
||||
} else
|
||||
{
|
||||
switch ( serializer.getVersion() )
|
||||
{
|
||||
case V1_21_5:
|
||||
if ( component.getHoverEvent().getAction() == HoverEvent.Action.SHOW_ITEM || component.getHoverEvent().getAction() == HoverEvent.Action.SHOW_ENTITY )
|
||||
{
|
||||
JsonObject inlined = context.serialize( ( component.getHoverEvent().getContents().size() == 1 )
|
||||
? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ).getAsJsonObject();
|
||||
inlined.entrySet().forEach( entry -> hoverEvent.add( entry.getKey(), entry.getValue() ) );
|
||||
} else
|
||||
{
|
||||
hoverEvent.add( "value", context.serialize( ( component.getHoverEvent().getContents().size() == 1 )
|
||||
? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ) );
|
||||
}
|
||||
break;
|
||||
case V1_16:
|
||||
hoverEvent.add( "contents", context.serialize( ( component.getHoverEvent().getContents().size() == 1 )
|
||||
? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ) );
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() );
|
||||
}
|
||||
}
|
||||
switch ( serializer.getVersion() )
|
||||
{
|
||||
case V1_21_5:
|
||||
object.add( "hover_event", hoverEvent );
|
||||
break;
|
||||
case V1_16:
|
||||
object.add( "hoverEvent", hoverEvent );
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( component.getExtra() != null )
|
||||
{
|
||||
object.add( "extra", context.serialize( component.getExtra() ) );
|
||||
}
|
||||
} finally
|
||||
{
|
||||
VersionedComponentSerializer.serializedComponents.get().remove( component );
|
||||
if ( first )
|
||||
{
|
||||
VersionedComponentSerializer.serializedComponents.set( null );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package net.md_5.bungee.chat;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public enum ChatVersion
|
||||
{
|
||||
V1_16,
|
||||
V1_21_5;
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
package net.md_5.bungee.chat;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import java.lang.reflect.Type;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ComponentStyle;
|
||||
import net.md_5.bungee.api.chat.hover.content.Content;
|
||||
|
||||
public class ComponentSerializer implements JsonDeserializer<BaseComponent>
|
||||
{
|
||||
|
||||
/**
|
||||
* Parse a JSON-compliant String as an array of base components. The input
|
||||
* can be one of either an array of components, or a single component
|
||||
* object. If the input is an array, each component will be parsed
|
||||
* individually and returned in the order that they were parsed. If the
|
||||
* input is a single component object, a single-valued array with the
|
||||
* component will be returned.
|
||||
* <p>
|
||||
* <strong>NOTE:</strong> {@link #deserialize(String)} is preferred as it
|
||||
* will parse only one component as opposed to an array of components which
|
||||
* is non- standard behavior. This method is still appropriate for parsing
|
||||
* multiple components at once, although such use case is rarely (if at all)
|
||||
* exhibited in vanilla Minecraft.
|
||||
*
|
||||
* @param json the component json to parse
|
||||
* @return an array of all parsed components
|
||||
*/
|
||||
public static BaseComponent[] parse(String json)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().parse( json );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a JSON-compliant String as a single component.
|
||||
*
|
||||
* @param json the component json to parse
|
||||
* @return the deserialized component
|
||||
* @throws IllegalArgumentException if anything other than a valid JSON
|
||||
* component string is passed as input
|
||||
*/
|
||||
public static BaseComponent deserialize(String json)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().deserialize( json );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a JSON element as a single component.
|
||||
*
|
||||
* @param jsonElement the component json to parse
|
||||
* @return the deserialized component
|
||||
* @throws IllegalArgumentException if anything other than a valid JSON
|
||||
* component is passed as input
|
||||
*/
|
||||
public static BaseComponent deserialize(JsonElement jsonElement)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().deserialize( jsonElement );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a JSON-compliant String as a component style.
|
||||
*
|
||||
* @param json the component style json to parse
|
||||
* @return the deserialized component style
|
||||
* @throws IllegalArgumentException if anything other than a valid JSON
|
||||
* component style string is passed as input
|
||||
*/
|
||||
public static ComponentStyle deserializeStyle(String json)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().deserializeStyle( json );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a JSON element as a component style.
|
||||
*
|
||||
* @param jsonElement the component style json to parse
|
||||
* @return the deserialized component style
|
||||
* @throws IllegalArgumentException if anything other than a valid JSON
|
||||
* component style is passed as input
|
||||
*/
|
||||
public static ComponentStyle deserializeStyle(JsonElement jsonElement)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().deserializeStyle( jsonElement );
|
||||
}
|
||||
|
||||
public static JsonElement toJson(BaseComponent component)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().toJson( component );
|
||||
}
|
||||
|
||||
public static JsonElement toJson(ComponentStyle style)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().toJson( style );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object the object to serialize
|
||||
* @return the JSON string representation of the object
|
||||
* @deprecated Error-prone, be careful which object you input here
|
||||
*/
|
||||
@Deprecated
|
||||
public static String toString(Object object)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().toString( object );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param content the content to serialize
|
||||
* @return the JSON string representation of the object
|
||||
* @deprecated for legacy internal use only
|
||||
*/
|
||||
@Deprecated
|
||||
public static String toString(Content content)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().toString( content );
|
||||
}
|
||||
|
||||
public static String toString(BaseComponent component)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().toString( component );
|
||||
}
|
||||
|
||||
public static String toString(BaseComponent... components)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().toString( components );
|
||||
}
|
||||
|
||||
public static String toString(ComponentStyle style)
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().toString( style );
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
return VersionedComponentSerializer.getDefault().deserialize( json, typeOfT, context );
|
||||
}
|
||||
}
|
@@ -0,0 +1,137 @@
|
||||
package net.md_5.bungee.chat;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.ComponentStyle;
|
||||
import net.md_5.bungee.api.chat.ComponentStyleBuilder;
|
||||
|
||||
public class ComponentStyleSerializer implements JsonSerializer<ComponentStyle>, JsonDeserializer<ComponentStyle>
|
||||
{
|
||||
|
||||
private static boolean getAsBoolean(JsonElement el)
|
||||
{
|
||||
if ( el.isJsonPrimitive() )
|
||||
{
|
||||
JsonPrimitive primitive = (JsonPrimitive) el;
|
||||
|
||||
if ( primitive.isBoolean() )
|
||||
{
|
||||
return primitive.getAsBoolean();
|
||||
}
|
||||
|
||||
if ( primitive.isNumber() )
|
||||
{
|
||||
Number number = primitive.getAsNumber();
|
||||
if ( number instanceof Byte )
|
||||
{
|
||||
return number.byteValue() != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void serializeTo(ComponentStyle style, JsonObject object)
|
||||
{
|
||||
if ( style.isBoldRaw() != null )
|
||||
{
|
||||
object.addProperty( "bold", style.isBoldRaw() );
|
||||
}
|
||||
if ( style.isItalicRaw() != null )
|
||||
{
|
||||
object.addProperty( "italic", style.isItalicRaw() );
|
||||
}
|
||||
if ( style.isUnderlinedRaw() != null )
|
||||
{
|
||||
object.addProperty( "underlined", style.isUnderlinedRaw() );
|
||||
}
|
||||
if ( style.isStrikethroughRaw() != null )
|
||||
{
|
||||
object.addProperty( "strikethrough", style.isStrikethroughRaw() );
|
||||
}
|
||||
if ( style.isObfuscatedRaw() != null )
|
||||
{
|
||||
object.addProperty( "obfuscated", style.isObfuscatedRaw() );
|
||||
}
|
||||
if ( style.hasColor() && style.getColor().getColor() != null )
|
||||
{
|
||||
object.addProperty( "color", style.getColor().getName() );
|
||||
}
|
||||
if ( style.hasShadowColor() )
|
||||
{
|
||||
object.addProperty( "shadow_color", style.getShadowColor().getRGB() );
|
||||
}
|
||||
if ( style.hasFont() )
|
||||
{
|
||||
object.addProperty( "font", style.getFont() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentStyle deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
ComponentStyleBuilder builder = ComponentStyle.builder();
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
for ( Map.Entry<String, JsonElement> entry : object.entrySet() )
|
||||
{
|
||||
String name = entry.getKey();
|
||||
JsonElement value = entry.getValue();
|
||||
switch ( name )
|
||||
{
|
||||
case "bold":
|
||||
builder.bold( getAsBoolean( value ) );
|
||||
break;
|
||||
case "italic":
|
||||
builder.italic( getAsBoolean( value ) );
|
||||
break;
|
||||
case "underlined":
|
||||
builder.underlined( getAsBoolean( value ) );
|
||||
break;
|
||||
case "strikethrough":
|
||||
builder.strikethrough( getAsBoolean( value ) );
|
||||
break;
|
||||
case "obfuscated":
|
||||
builder.obfuscated( getAsBoolean( value ) );
|
||||
break;
|
||||
case "color":
|
||||
builder.color( ChatColor.of( value.getAsString() ) );
|
||||
break;
|
||||
case "shadow_color":
|
||||
if ( value.isJsonArray() )
|
||||
{
|
||||
JsonArray array = value.getAsJsonArray();
|
||||
|
||||
builder.shadowColor( new Color( array.get( 0 ).getAsFloat(), array.get( 1 ).getAsFloat(), array.get( 2 ).getAsFloat(), array.get( 3 ).getAsFloat() ) );
|
||||
} else if ( value.isJsonPrimitive() )
|
||||
{
|
||||
builder.shadowColor( new Color( value.getAsNumber().intValue(), true ) );
|
||||
}
|
||||
break;
|
||||
case "font":
|
||||
builder.font( value.getAsString() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(ComponentStyle src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
JsonObject object = new JsonObject();
|
||||
serializeTo( src, object );
|
||||
return object;
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package net.md_5.bungee.chat;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import net.md_5.bungee.api.chat.KeybindComponent;
|
||||
|
||||
public class KeybindComponentSerializer extends BaseComponentSerializer implements JsonSerializer<KeybindComponent>, JsonDeserializer<KeybindComponent>
|
||||
{
|
||||
|
||||
public KeybindComponentSerializer(VersionedComponentSerializer serializer)
|
||||
{
|
||||
super( serializer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeybindComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
JsonElement keybind = object.get( "keybind" );
|
||||
if ( keybind == null )
|
||||
{
|
||||
throw new JsonParseException( "Could not parse JSON: missing 'keybind' property" );
|
||||
}
|
||||
KeybindComponent component = new KeybindComponent();
|
||||
deserialize( object, component, context );
|
||||
component.setKeybind( keybind.getAsString() );
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(KeybindComponent src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
JsonObject object = new JsonObject();
|
||||
serialize( object, src, context );
|
||||
object.addProperty( "keybind", src.getKeybind() );
|
||||
return object;
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
package net.md_5.bungee.chat;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import net.md_5.bungee.api.chat.ScoreComponent;
|
||||
|
||||
public class ScoreComponentSerializer extends BaseComponentSerializer implements JsonSerializer<ScoreComponent>, JsonDeserializer<ScoreComponent>
|
||||
{
|
||||
|
||||
public ScoreComponentSerializer(VersionedComponentSerializer serializer)
|
||||
{
|
||||
super( serializer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScoreComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
JsonObject json = element.getAsJsonObject();
|
||||
JsonObject score = json.getAsJsonObject( "score" );
|
||||
if ( score == null )
|
||||
{
|
||||
throw new JsonParseException( "Could not parse JSON: missing 'score' property" );
|
||||
}
|
||||
JsonElement nameJson = score.get( "name" );
|
||||
if ( nameJson == null )
|
||||
{
|
||||
throw new JsonParseException( "A score component needs at least a name (and an objective)" );
|
||||
}
|
||||
JsonElement objectiveJson = score.get( "objective" );
|
||||
if ( objectiveJson == null )
|
||||
{
|
||||
throw new JsonParseException( "A score component needs at least a name and an objective" );
|
||||
}
|
||||
|
||||
String name = nameJson.getAsString();
|
||||
String objective = objectiveJson.getAsString();
|
||||
ScoreComponent component = new ScoreComponent( name, objective );
|
||||
JsonElement value = score.get( "value" );
|
||||
if ( value != null && !value.getAsString().isEmpty() )
|
||||
{
|
||||
component.setValue( value.getAsString() );
|
||||
}
|
||||
|
||||
deserialize( json, component, context );
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(ScoreComponent component, Type type, JsonSerializationContext context)
|
||||
{
|
||||
JsonObject root = new JsonObject();
|
||||
serialize( root, component, context );
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty( "name", component.getName() );
|
||||
json.addProperty( "objective", component.getObjective() );
|
||||
json.addProperty( "value", component.getValue() );
|
||||
root.add( "score", json );
|
||||
return root;
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
package net.md_5.bungee.chat;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import net.md_5.bungee.api.chat.SelectorComponent;
|
||||
|
||||
public class SelectorComponentSerializer extends BaseComponentSerializer implements JsonSerializer<SelectorComponent>, JsonDeserializer<SelectorComponent>
|
||||
{
|
||||
|
||||
public SelectorComponentSerializer(VersionedComponentSerializer serializer)
|
||||
{
|
||||
super( serializer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectorComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
JsonElement selector = object.get( "selector" );
|
||||
if ( selector == null )
|
||||
{
|
||||
throw new JsonParseException( "Could not parse JSON: missing 'selector' property" );
|
||||
}
|
||||
SelectorComponent component = new SelectorComponent( selector.getAsString() );
|
||||
|
||||
JsonElement separator = object.get( "separator" );
|
||||
if ( separator != null )
|
||||
{
|
||||
component.setSeparator( serializer.deserialize( separator.getAsString() ) );
|
||||
}
|
||||
|
||||
deserialize( object, component, context );
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(SelectorComponent component, Type type, JsonSerializationContext context)
|
||||
{
|
||||
JsonObject object = new JsonObject();
|
||||
serialize( object, component, context );
|
||||
object.addProperty( "selector", component.getSelector() );
|
||||
|
||||
if ( component.getSeparator() != null )
|
||||
{
|
||||
object.addProperty( "separator", serializer.toString( component.getSeparator() ) );
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package net.md_5.bungee.chat;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
|
||||
public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TextComponent>, JsonDeserializer<TextComponent>
|
||||
{
|
||||
|
||||
public TextComponentSerializer(VersionedComponentSerializer serializer)
|
||||
{
|
||||
super( serializer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
TextComponent component = new TextComponent();
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
JsonElement text = object.get( "text" );
|
||||
if ( text != null )
|
||||
{
|
||||
component.setText( text.getAsString() );
|
||||
}
|
||||
deserialize( object, component, context );
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(TextComponent src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
JsonObject object = new JsonObject();
|
||||
serialize( object, src, context );
|
||||
object.addProperty( "text", src.getText() );
|
||||
return object;
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package net.md_5.bungee.chat;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
|
||||
public class TranslatableComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TranslatableComponent>, JsonDeserializer<TranslatableComponent>
|
||||
{
|
||||
|
||||
public TranslatableComponentSerializer(VersionedComponentSerializer serializer)
|
||||
{
|
||||
super( serializer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TranslatableComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
TranslatableComponent component = new TranslatableComponent();
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
deserialize( object, component, context );
|
||||
JsonElement translate = object.get( "translate" );
|
||||
if ( translate == null )
|
||||
{
|
||||
throw new JsonParseException( "Could not parse JSON: missing 'translate' property" );
|
||||
}
|
||||
component.setTranslate( translate.getAsString() );
|
||||
JsonElement with = object.get( "with" );
|
||||
if ( with != null )
|
||||
{
|
||||
component.setWith( Arrays.asList( context.deserialize( with, BaseComponent[].class ) ) );
|
||||
}
|
||||
JsonElement fallback = object.get( "fallback" );
|
||||
if ( fallback != null )
|
||||
{
|
||||
component.setFallback( fallback.getAsString() );
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(TranslatableComponent src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
JsonObject object = new JsonObject();
|
||||
serialize( object, src, context );
|
||||
object.addProperty( "translate", src.getTranslate() );
|
||||
if ( src.getWith() != null )
|
||||
{
|
||||
object.add( "with", context.serialize( src.getWith() ) );
|
||||
}
|
||||
if ( src.getFallback() != null )
|
||||
{
|
||||
object.addProperty( "fallback", src.getFallback() );
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
@@ -0,0 +1,280 @@
|
||||
package net.md_5.bungee.chat;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Set;
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ComponentStyle;
|
||||
import net.md_5.bungee.api.chat.ItemTag;
|
||||
import net.md_5.bungee.api.chat.KeybindComponent;
|
||||
import net.md_5.bungee.api.chat.ScoreComponent;
|
||||
import net.md_5.bungee.api.chat.SelectorComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
import net.md_5.bungee.api.chat.hover.content.Content;
|
||||
import net.md_5.bungee.api.chat.hover.content.Entity;
|
||||
import net.md_5.bungee.api.chat.hover.content.EntitySerializer;
|
||||
import net.md_5.bungee.api.chat.hover.content.Item;
|
||||
import net.md_5.bungee.api.chat.hover.content.ItemSerializer;
|
||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||
import net.md_5.bungee.api.chat.hover.content.TextSerializer;
|
||||
import net.md_5.bungee.api.dialog.Dialog;
|
||||
import net.md_5.bungee.api.dialog.chat.ShowDialogClickEvent;
|
||||
import net.md_5.bungee.dialog.DialogSerializer;
|
||||
import net.md_5.bungee.dialog.ShowDialogClickEventSerializer;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public class VersionedComponentSerializer implements JsonDeserializer<BaseComponent>
|
||||
{
|
||||
|
||||
@Getter
|
||||
@ApiStatus.Internal
|
||||
private final Gson gson;
|
||||
@Getter
|
||||
@ApiStatus.Internal
|
||||
private final ChatVersion version;
|
||||
@Getter
|
||||
@ApiStatus.Internal
|
||||
private final DialogSerializer dialogSerializer;
|
||||
|
||||
public VersionedComponentSerializer(ChatVersion version)
|
||||
{
|
||||
this.version = version;
|
||||
this.dialogSerializer = new DialogSerializer( this );
|
||||
this.gson = new GsonBuilder().
|
||||
registerTypeAdapter( BaseComponent.class, this ).
|
||||
registerTypeAdapter( TextComponent.class, new TextComponentSerializer( this ) ).
|
||||
registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer( this ) ).
|
||||
registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer( this ) ).
|
||||
registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer( this ) ).
|
||||
registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer( this ) ).
|
||||
registerTypeAdapter( ComponentStyle.class, new ComponentStyleSerializer() ).
|
||||
registerTypeAdapter( Entity.class, new EntitySerializer( this ) ).
|
||||
registerTypeAdapter( Text.class, new TextSerializer() ).
|
||||
registerTypeAdapter( Item.class, new ItemSerializer() ).
|
||||
registerTypeAdapter( ItemTag.class, new ItemTag.Serializer() ).
|
||||
// Dialogs
|
||||
registerTypeAdapter( Dialog.class, dialogSerializer ).
|
||||
registerTypeAdapter( ShowDialogClickEvent.class, new ShowDialogClickEventSerializer() ).
|
||||
create();
|
||||
}
|
||||
|
||||
private static final VersionedComponentSerializer v1_16 = new VersionedComponentSerializer( ChatVersion.V1_16 );
|
||||
private static final VersionedComponentSerializer v1_21_5 = new VersionedComponentSerializer( ChatVersion.V1_21_5 );
|
||||
|
||||
public static VersionedComponentSerializer forVersion(ChatVersion version)
|
||||
{
|
||||
switch ( version )
|
||||
{
|
||||
case V1_16:
|
||||
return v1_16;
|
||||
case V1_21_5:
|
||||
return v1_21_5;
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unknown version " + version );
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ApiStatus.Internal
|
||||
public static VersionedComponentSerializer getDefault()
|
||||
{
|
||||
return v1_16;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static final ThreadLocal<Set<BaseComponent>> serializedComponents = new ThreadLocal<Set<BaseComponent>>();
|
||||
|
||||
/**
|
||||
* Parse a JSON-compliant String as an array of base components. The input
|
||||
* can be one of either an array of components, or a single component
|
||||
* object. If the input is an array, each component will be parsed
|
||||
* individually and returned in the order that they were parsed. If the
|
||||
* input is a single component object, a single-valued array with the
|
||||
* component will be returned.
|
||||
* <p>
|
||||
* <strong>NOTE:</strong> {@link #deserialize(String)} is preferred as it
|
||||
* will parse only one component as opposed to an array of components which
|
||||
* is non- standard behavior. This method is still appropriate for parsing
|
||||
* multiple components at once, although such use case is rarely (if at all)
|
||||
* exhibited in vanilla Minecraft.
|
||||
*
|
||||
* @param json the component json to parse
|
||||
* @return an array of all parsed components
|
||||
*/
|
||||
public BaseComponent[] parse(String json)
|
||||
{
|
||||
JsonElement jsonElement = JsonParser.parseString( json );
|
||||
|
||||
if ( jsonElement.isJsonArray() )
|
||||
{
|
||||
return gson.fromJson( jsonElement, BaseComponent[].class );
|
||||
} else
|
||||
{
|
||||
return new BaseComponent[]
|
||||
{
|
||||
gson.fromJson( jsonElement, BaseComponent.class )
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a JSON-compliant String as a single component.
|
||||
*
|
||||
* @param json the component json to parse
|
||||
* @return the deserialized component
|
||||
* @throws IllegalArgumentException if anything other than a valid JSON
|
||||
* component string is passed as input
|
||||
*/
|
||||
public BaseComponent deserialize(String json)
|
||||
{
|
||||
JsonElement jsonElement = JsonParser.parseString( json );
|
||||
|
||||
return deserialize( jsonElement );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a JSON element as a single component.
|
||||
*
|
||||
* @param jsonElement the component json to parse
|
||||
* @return the deserialized component
|
||||
* @throws IllegalArgumentException if anything other than a valid JSON
|
||||
* component is passed as input
|
||||
*/
|
||||
public BaseComponent deserialize(JsonElement jsonElement)
|
||||
{
|
||||
if ( jsonElement instanceof JsonPrimitive )
|
||||
{
|
||||
JsonPrimitive primitive = (JsonPrimitive) jsonElement;
|
||||
if ( primitive.isString() )
|
||||
{
|
||||
return new TextComponent( primitive.getAsString() );
|
||||
}
|
||||
} else if ( jsonElement instanceof JsonArray )
|
||||
{
|
||||
BaseComponent[] array = gson.fromJson( jsonElement, BaseComponent[].class );
|
||||
return TextComponent.fromArray( array );
|
||||
}
|
||||
|
||||
return gson.fromJson( jsonElement, BaseComponent.class );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a JSON-compliant String as a component style.
|
||||
*
|
||||
* @param json the component style json to parse
|
||||
* @return the deserialized component style
|
||||
* @throws IllegalArgumentException if anything other than a valid JSON
|
||||
* component style string is passed as input
|
||||
*/
|
||||
public ComponentStyle deserializeStyle(String json)
|
||||
{
|
||||
JsonElement jsonElement = JsonParser.parseString( json );
|
||||
|
||||
return deserializeStyle( jsonElement );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a JSON element as a component style.
|
||||
*
|
||||
* @param jsonElement the component style json to parse
|
||||
* @return the deserialized component style
|
||||
* @throws IllegalArgumentException if anything other than a valid JSON
|
||||
* component style is passed as input
|
||||
*/
|
||||
public ComponentStyle deserializeStyle(JsonElement jsonElement)
|
||||
{
|
||||
return gson.fromJson( jsonElement, ComponentStyle.class );
|
||||
}
|
||||
|
||||
public JsonElement toJson(BaseComponent component)
|
||||
{
|
||||
return gson.toJsonTree( component );
|
||||
}
|
||||
|
||||
public JsonElement toJson(ComponentStyle style)
|
||||
{
|
||||
return gson.toJsonTree( style );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object the object to serialize
|
||||
* @return the JSON string representation of the object
|
||||
* @deprecated Error-prone, be careful which object you input here
|
||||
*/
|
||||
@Deprecated
|
||||
public String toString(Object object)
|
||||
{
|
||||
return gson.toJson( object );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param content the content to serialize
|
||||
* @return the JSON string representation of the object
|
||||
* @deprecated for legacy internal use only
|
||||
*/
|
||||
@Deprecated
|
||||
public String toString(Content content)
|
||||
{
|
||||
return gson.toJson( content );
|
||||
}
|
||||
|
||||
public String toString(BaseComponent component)
|
||||
{
|
||||
return gson.toJson( component );
|
||||
}
|
||||
|
||||
public String toString(BaseComponent... components)
|
||||
{
|
||||
if ( components.length == 1 )
|
||||
{
|
||||
return gson.toJson( components[0] );
|
||||
} else
|
||||
{
|
||||
return gson.toJson( new TextComponent( components ) );
|
||||
}
|
||||
}
|
||||
|
||||
public String toString(ComponentStyle style)
|
||||
{
|
||||
return gson.toJson( style );
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
if ( json.isJsonPrimitive() )
|
||||
{
|
||||
return new TextComponent( json.getAsString() );
|
||||
}
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
if ( object.has( "translate" ) )
|
||||
{
|
||||
return context.deserialize( json, TranslatableComponent.class );
|
||||
}
|
||||
if ( object.has( "keybind" ) )
|
||||
{
|
||||
return context.deserialize( json, KeybindComponent.class );
|
||||
}
|
||||
if ( object.has( "score" ) )
|
||||
{
|
||||
return context.deserialize( json, ScoreComponent.class );
|
||||
}
|
||||
if ( object.has( "selector" ) )
|
||||
{
|
||||
return context.deserialize( json, SelectorComponent.class );
|
||||
}
|
||||
return context.deserialize( json, TextComponent.class );
|
||||
}
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
package net.md_5.bungee.dialog;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.dialog.ConfirmationDialog;
|
||||
import net.md_5.bungee.api.dialog.Dialog;
|
||||
import net.md_5.bungee.api.dialog.DialogBase;
|
||||
import net.md_5.bungee.api.dialog.DialogListDialog;
|
||||
import net.md_5.bungee.api.dialog.MultiActionDialog;
|
||||
import net.md_5.bungee.api.dialog.MultiActionInputFormDialog;
|
||||
import net.md_5.bungee.api.dialog.NoticeDialog;
|
||||
import net.md_5.bungee.api.dialog.ServerLinksDialog;
|
||||
import net.md_5.bungee.api.dialog.SimpleInputFormDialog;
|
||||
import net.md_5.bungee.chat.VersionedComponentSerializer;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class DialogSerializer implements JsonDeserializer<Dialog>, JsonSerializer<Dialog>
|
||||
{
|
||||
|
||||
private static final BiMap<String, Class<? extends Dialog>> TYPES;
|
||||
private final VersionedComponentSerializer serializer;
|
||||
|
||||
static
|
||||
{
|
||||
ImmutableBiMap.Builder<String, Class<? extends Dialog>> builder = ImmutableBiMap.builder();
|
||||
|
||||
builder.put( "minecraft:notice", NoticeDialog.class );
|
||||
builder.put( "minecraft:confirmation", ConfirmationDialog.class );
|
||||
builder.put( "minecraft:multi_action", MultiActionDialog.class );
|
||||
builder.put( "minecraft:server_links", ServerLinksDialog.class );
|
||||
builder.put( "minecraft:dialog_list", DialogListDialog.class );
|
||||
builder.put( "minecraft:simple_input_form", SimpleInputFormDialog.class );
|
||||
builder.put( "minecraft:multi_action_input_form", MultiActionInputFormDialog.class );
|
||||
|
||||
TYPES = builder.build();
|
||||
}
|
||||
|
||||
public JsonElement toJson(Dialog dialog)
|
||||
{
|
||||
return serializer.getGson().toJsonTree( dialog, Dialog.class );
|
||||
}
|
||||
|
||||
public String toString(Dialog dialog)
|
||||
{
|
||||
return serializer.getGson().toJson( dialog, Dialog.class );
|
||||
}
|
||||
|
||||
public Dialog deserialize(JsonElement jsonElement)
|
||||
{
|
||||
return serializer.getGson().fromJson( jsonElement, Dialog.class );
|
||||
}
|
||||
|
||||
public Dialog deserialize(String json)
|
||||
{
|
||||
JsonElement jsonElement = JsonParser.parseString( json );
|
||||
return deserialize( jsonElement );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
|
||||
String type = object.get( "type" ).getAsString();
|
||||
|
||||
if ( object.has( "base" ) )
|
||||
{
|
||||
throw new JsonParseException( "Cannot explicitly specify base" );
|
||||
}
|
||||
|
||||
Type realType = TYPES.get( type );
|
||||
if ( realType == null )
|
||||
{
|
||||
throw new JsonParseException( "Unknown type " + type );
|
||||
}
|
||||
|
||||
Dialog dialog = context.deserialize( json, realType );
|
||||
|
||||
DialogBase base = context.deserialize( json, DialogBase.class );
|
||||
dialog.setBase( base );
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Dialog src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
if ( src == null )
|
||||
{
|
||||
return JsonNull.INSTANCE;
|
||||
}
|
||||
|
||||
Class<? extends Dialog> realType = src.getClass();
|
||||
String type = TYPES.inverse().get( realType );
|
||||
Preconditions.checkArgument( type != null, "Unknown type %s", typeOfSrc );
|
||||
|
||||
JsonObject object = (JsonObject) context.serialize( src, realType );
|
||||
object.addProperty( "type", type );
|
||||
|
||||
JsonObject base = (JsonObject) context.serialize( src.getBase() );
|
||||
object.asMap().putAll( base.asMap() );
|
||||
|
||||
return object;
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package net.md_5.bungee.dialog;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import net.md_5.bungee.api.dialog.Dialog;
|
||||
import net.md_5.bungee.api.dialog.chat.ShowDialogClickEvent;
|
||||
|
||||
public class ShowDialogClickEventSerializer implements JsonDeserializer<ShowDialogClickEvent>, JsonSerializer<ShowDialogClickEvent>
|
||||
{
|
||||
|
||||
@Override
|
||||
public ShowDialogClickEvent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
if ( json.isJsonPrimitive() && json.getAsJsonPrimitive().isString() )
|
||||
{
|
||||
return new ShowDialogClickEvent( json.getAsJsonPrimitive().getAsString() );
|
||||
}
|
||||
|
||||
return new ShowDialogClickEvent( (Dialog) context.deserialize( json, Dialog.class ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(ShowDialogClickEvent src, Type typeOfSrc, JsonSerializationContext context)
|
||||
{
|
||||
if ( src.getReference() != null )
|
||||
{
|
||||
return new JsonPrimitive( src.getReference() );
|
||||
}
|
||||
|
||||
return context.serialize( src.getDialog(), Dialog.class );
|
||||
}
|
||||
}
|
@@ -0,0 +1,871 @@
|
||||
package net.md_5.bungee.api.chat;
|
||||
|
||||
import static net.md_5.bungee.api.ChatColor.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import java.awt.Color;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ObjIntConsumer;
|
||||
import java.util.function.Supplier;
|
||||
import net.md_5.bungee.api.chat.hover.content.Entity;
|
||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ComponentsTest
|
||||
{
|
||||
|
||||
public static void testDissembleReassemble(BaseComponent[] components)
|
||||
{
|
||||
String json = ComponentSerializer.toString( components );
|
||||
BaseComponent[] parsed = ComponentSerializer.parse( json );
|
||||
assertEquals( BaseComponent.toLegacyText( parsed ), BaseComponent.toLegacyText( components ) );
|
||||
}
|
||||
|
||||
public static void testDissembleReassemble(BaseComponent component)
|
||||
{
|
||||
String json = ComponentSerializer.toString( component );
|
||||
BaseComponent[] parsed = ComponentSerializer.parse( json );
|
||||
assertEquals( BaseComponent.toLegacyText( parsed ), BaseComponent.toLegacyText( component ) );
|
||||
}
|
||||
|
||||
public static void testAssembleDissemble(String json, boolean modern)
|
||||
{
|
||||
if ( modern )
|
||||
{
|
||||
BaseComponent deserialized = ComponentSerializer.deserialize( json );
|
||||
assertEquals( json, ComponentSerializer.toString( deserialized ) );
|
||||
} else
|
||||
{
|
||||
BaseComponent[] parsed = ComponentSerializer.parse( json );
|
||||
assertEquals( json, ComponentSerializer.toString( parsed ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItemParse()
|
||||
{
|
||||
// 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
|
||||
} );
|
||||
testDissembleReassemble( textComponent );
|
||||
json = "{\"hoverEvent\":{\"action\":\"show_item\",\"value\":[{\"text\":\"{id:\\\"minecraft:netherrack\\\",Count:47b}\"}]},\"text\":\"Test\"}";
|
||||
testAssembleDissemble( json, false );
|
||||
testAssembleDissemble( json, true );
|
||||
//////////
|
||||
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 ) );
|
||||
assertEquals( hoverVal, ComponentSerializer.toString( (BaseComponent[]) contentText.getValue() ) );
|
||||
testDissembleReassemble( components );
|
||||
//////////
|
||||
// TODO: now ambiguous since "text" to distinguish Text from Item is not required
|
||||
/*
|
||||
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}}";
|
||||
Item contentItem = new Item( "minecraft:wood", 1, ItemTag.ofNbt( nbt ) );
|
||||
HoverEvent hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_ITEM, contentItem );
|
||||
component1.setHoverEvent( hoverEvent );
|
||||
json = ComponentSerializer.toString( component1 );
|
||||
components = ComponentSerializer.parse( json );
|
||||
Item parsedContentItem = ( (Item) components[0].getHoverEvent().getContents().get( 0 ) );
|
||||
assertEquals( contentItem, parsedContentItem );
|
||||
assertEquals( contentItem.getCount(), parsedContentItem.getCount() );
|
||||
assertEquals( contentItem.getId(), parsedContentItem.getId() );
|
||||
assertEquals( nbt, parsedContentItem.getTag().getNbt() );
|
||||
*/
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArrayUUIDParse()
|
||||
{
|
||||
BaseComponent[] uuidComponent = ComponentSerializer.parse( "{\"translate\":\"multiplayer.player.joined\",\"with\":[{\"text\":\"Rexcantor64\",\"hoverEvent\":{\"contents\":{\"type\":\"minecraft:player\",\"id\":[1328556382,-2138814985,-1895806765,-1039963041],\"name\":\"Rexcantor64\"},\"action\":\"show_entity\"},\"insertion\":\"Rexcantor64\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":\"/tell Rexcantor64 \"}}],\"color\":\"yellow\"}" );
|
||||
assertEquals( "4f30295e-8084-45f7-8f00-48d3c2036c5f", ( (Entity) ( (TranslatableComponent) uuidComponent[0] ).getWith().get( 0 ).getHoverEvent().getContents().get( 0 ) ).getId() );
|
||||
testDissembleReassemble( uuidComponent );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyComponentBuilderCreate()
|
||||
{
|
||||
testEmptyComponentBuilder(
|
||||
ComponentBuilder::create,
|
||||
(components) -> assertEquals( components.length, 0 ),
|
||||
(components, size) -> assertEquals( size, components.length )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyComponentBuilderBuild()
|
||||
{
|
||||
testEmptyComponentBuilder(
|
||||
ComponentBuilder::build,
|
||||
(component) -> assertNull( component.getExtra() ),
|
||||
(component, size) -> assertEquals( component.getExtra().size(), size )
|
||||
);
|
||||
}
|
||||
|
||||
private static <T> void testEmptyComponentBuilder(Function<ComponentBuilder, T> componentBuilder, Consumer<T> emptyAssertion, ObjIntConsumer<T> sizedAssertion)
|
||||
{
|
||||
ComponentBuilder builder = new ComponentBuilder();
|
||||
|
||||
T component = componentBuilder.apply( builder );
|
||||
emptyAssertion.accept( component );
|
||||
|
||||
for ( int i = 0; i < 3; i++ )
|
||||
{
|
||||
builder.append( "part:" + i );
|
||||
component = componentBuilder.apply( builder );
|
||||
sizedAssertion.accept( component, i + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDummyRetaining()
|
||||
{
|
||||
ComponentBuilder builder = new ComponentBuilder();
|
||||
assertNotNull( builder.getCurrentComponent() );
|
||||
builder.color( GREEN );
|
||||
builder.append( "test ", ComponentBuilder.FormatRetention.ALL );
|
||||
assertEquals( builder.getCurrentComponent().getColor(), GREEN );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComponentGettingExceptions()
|
||||
{
|
||||
ComponentBuilder builder = new ComponentBuilder();
|
||||
assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( -1 ) );
|
||||
assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( 0 ) );
|
||||
assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( 1 ) );
|
||||
BaseComponent component = new TextComponent( "Hello" );
|
||||
builder.append( component );
|
||||
assertEquals( builder.getComponent( 0 ), component );
|
||||
assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( 1 ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatNotColor()
|
||||
{
|
||||
BaseComponent[] component = new ComponentBuilder().color( BOLD ).append( "Test" ).create();
|
||||
|
||||
String json = ComponentSerializer.toString( component );
|
||||
BaseComponent[] parsed = ComponentSerializer.parse( json );
|
||||
|
||||
assertNull( parsed[0].getColorRaw(), "Format should not be preserved as color" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComponentParting()
|
||||
{
|
||||
ComponentBuilder builder = new ComponentBuilder();
|
||||
TextComponent apple = new TextComponent( "apple" );
|
||||
builder.append( apple );
|
||||
assertEquals( builder.getCurrentComponent(), apple );
|
||||
assertEquals( builder.getComponent( 0 ), apple );
|
||||
|
||||
TextComponent mango = new TextComponent( "mango" );
|
||||
TextComponent orange = new TextComponent( "orange" );
|
||||
builder.append( mango );
|
||||
builder.append( orange );
|
||||
builder.removeComponent( 1 ); // Removing mango
|
||||
assertEquals( builder.getComponent( 0 ), apple );
|
||||
assertEquals( builder.getComponent( 1 ), orange );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToLegacyFromLegacy()
|
||||
{
|
||||
String text = "" + GREEN + BOLD + "Hello " + WHITE + MAGIC + "world" + GRAY + "!";
|
||||
assertEquals( text, BaseComponent.toLegacyText( TextComponent.fromLegacyText( text ) ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComponentBuilderCursorInvalidPos()
|
||||
{
|
||||
ComponentBuilder builder = new ComponentBuilder();
|
||||
builder.append( new TextComponent( "Apple, " ) );
|
||||
builder.append( new TextComponent( "Orange, " ) );
|
||||
assertThrows( IndexOutOfBoundsException.class, () -> builder.setCursor( -1 ) );
|
||||
assertThrows( IndexOutOfBoundsException.class, () -> builder.setCursor( 2 ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComponentBuilderCursor()
|
||||
{
|
||||
TextComponent t1, t2, t3;
|
||||
ComponentBuilder builder = new ComponentBuilder();
|
||||
assertEquals( builder.getCursor(), -1 );
|
||||
builder.append( t1 = new TextComponent( "Apple, " ) );
|
||||
assertEquals( builder.getCursor(), 0 );
|
||||
builder.append( t2 = new TextComponent( "Orange, " ) );
|
||||
builder.append( t3 = new TextComponent( "Mango, " ) );
|
||||
assertEquals( builder.getCursor(), 2 );
|
||||
|
||||
builder.setCursor( 0 );
|
||||
assertEquals( builder.getCurrentComponent(), t1 );
|
||||
|
||||
// Test that appending new components updates the position to the new list size
|
||||
// after having previously set it to 0 (first component)
|
||||
builder.append( new TextComponent( "and Grapefruit" ) );
|
||||
assertEquals( builder.getCursor(), 3 );
|
||||
|
||||
builder.setCursor( 0 );
|
||||
builder.resetCursor();
|
||||
assertEquals( builder.getCursor(), 3 );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLegacyComponentBuilderAppend()
|
||||
{
|
||||
String text = "" + GREEN + BOLD + "Hello " + RESET + MAGIC + "world" + GRAY + "!";
|
||||
BaseComponent[] components = TextComponent.fromLegacyText( text );
|
||||
BaseComponent[] builderComponents = new ComponentBuilder().append( components ).create();
|
||||
assertArrayEquals( components, builderComponents );
|
||||
}
|
||||
|
||||
/*
|
||||
@Test
|
||||
public void testItemTag()
|
||||
{
|
||||
TextComponent component = new TextComponent( "Hello world" );
|
||||
HoverEvent.ContentItem content = new HoverEvent.ContentItem();
|
||||
content.setId( "minecraft:diamond_sword" );
|
||||
content.setCount( 1 );
|
||||
content.setTag( ItemTag.builder()
|
||||
.ench( new ItemTag.Enchantment( 5, 16 ) )
|
||||
.name( new TextComponent( "Sharp Sword" ) )
|
||||
.unbreakable( true )
|
||||
.lore( new ComponentBuilder( "Line1" ).create() )
|
||||
.lore( new ComponentBuilder( "Line2" ).create() )
|
||||
.build() );
|
||||
HoverEvent event = new HoverEvent( HoverEvent.Action.SHOW_ITEM, content );
|
||||
component.setHoverEvent( event );
|
||||
String serialised = ComponentSerializer.toString( component );
|
||||
BaseComponent[] deserialised = ComponentSerializer.parse( serialised );
|
||||
assertEquals( TextComponent.toLegacyText( deserialised ), TextComponent.toLegacyText( component ) );
|
||||
}
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testModernShowAdvancement()
|
||||
{
|
||||
String advancement = "achievement.openInventory";
|
||||
// First do the text using the newer contents system
|
||||
HoverEvent hoverEvent = new HoverEvent(
|
||||
HoverEvent.Action.SHOW_TEXT,
|
||||
new Text( advancement )
|
||||
);
|
||||
TextComponent component = new TextComponent( "test" );
|
||||
component.setHoverEvent( hoverEvent );
|
||||
assertEquals( component.getHoverEvent().getContents().size(), 1 );
|
||||
assertTrue( component.getHoverEvent().getContents().get( 0 ) instanceof Text );
|
||||
assertEquals( ( (Text) component.getHoverEvent().getContents().get( 0 ) ).getValue(), advancement );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHoverEventContentsCreate()
|
||||
{
|
||||
// First do the text using the newer contents system
|
||||
HoverEvent hoverEvent = new HoverEvent(
|
||||
HoverEvent.Action.SHOW_TEXT,
|
||||
new Text( new ComponentBuilder( "First" ).create() ),
|
||||
new Text( new ComponentBuilder( "Second" ).create() )
|
||||
);
|
||||
|
||||
this.testHoverEventContents(
|
||||
hoverEvent,
|
||||
ComponentSerializer::parse,
|
||||
(components) -> components[0].getHoverEvent(),
|
||||
ComponentsTest::testDissembleReassemble // BaseComponent
|
||||
);
|
||||
|
||||
// check the test still works with the value method
|
||||
hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Sample text" ).create() );
|
||||
TextComponent component = new TextComponent( "Sample text" );
|
||||
component.setHoverEvent( hoverEvent );
|
||||
|
||||
assertEquals( hoverEvent.getContents().size(), 1 );
|
||||
assertTrue( hoverEvent.isLegacy() );
|
||||
String serialized = ComponentSerializer.toString( component );
|
||||
BaseComponent[] deserialized = ComponentSerializer.parse( serialized );
|
||||
assertEquals( component.getHoverEvent(), deserialized[0].getHoverEvent() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHoverEventContentsBuild()
|
||||
{
|
||||
// First do the text using the newer contents system
|
||||
HoverEvent hoverEvent = new HoverEvent(
|
||||
HoverEvent.Action.SHOW_TEXT,
|
||||
new Text( new ComponentBuilder( "First" ).build() ),
|
||||
new Text( new ComponentBuilder( "Second" ).build() )
|
||||
);
|
||||
|
||||
this.testHoverEventContents(
|
||||
hoverEvent,
|
||||
ComponentSerializer::deserialize,
|
||||
BaseComponent::getHoverEvent,
|
||||
ComponentsTest::testDissembleReassemble // BaseComponent
|
||||
);
|
||||
}
|
||||
|
||||
private <T> void testHoverEventContents(HoverEvent hoverEvent, Function<String, T> deserializer, Function<T, HoverEvent> hoverEventGetter, Consumer<T> dissembleReassembleTest)
|
||||
{
|
||||
TextComponent component = new TextComponent( "Sample text" );
|
||||
component.setHoverEvent( hoverEvent );
|
||||
assertEquals( hoverEvent.getContents().size(), 2 );
|
||||
assertFalse( hoverEvent.isLegacy() );
|
||||
|
||||
String serialized = ComponentSerializer.toString( component );
|
||||
T deserialized = deserializer.apply( serialized );
|
||||
assertEquals( component.getHoverEvent(), hoverEventGetter.apply( deserialized ) );
|
||||
|
||||
// Test single content:
|
||||
String json = "{\"italic\":true,\"color\":\"gray\",\"translate\":\"chat.type.admin\",\"with\":[{\"text\":\"@\"}"
|
||||
+ ",{\"translate\":\"commands.give.success.single\",\"with\":[\"1\",{\"color\":\"white\""
|
||||
+ ",\"hoverEvent\":{\"action\":\"show_item\",\"contents\":{\"id\":\"minecraft:diamond_sword\",\"tag\":\""
|
||||
+ "{Damage:0,display:{Lore:['\\\"test lore'!\\\"'],Name:'\\\"test\\\"'}}\"}},"
|
||||
+ "\"extra\":[{\"italic\":true,\"extra\":[{\"text\":\"test\"}],\"text\":\"\"},{\"text\":\"]\"}],"
|
||||
+ "\"text\":\"[\"},{\"insertion\":\"Name\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":"
|
||||
+ "\"/tell Name \"},\"hoverEvent\":{\"action\":\"show_entity\",\"contents\":"
|
||||
+ "{\"type\":\"minecraft:player\",\"id\":\"00000000-0000-0000-0000-00000000000000\",\"name\":"
|
||||
+ "{\"text\":\"Name\"}}},\"text\":\"Name\"}]}]}";
|
||||
dissembleReassembleTest.accept( deserializer.apply( json ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatRetentionCopyFormattingCreate()
|
||||
{
|
||||
testFormatRetentionCopyFormatting( () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Test" ).create() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatRetentionCopyFormattingBuild()
|
||||
{
|
||||
testFormatRetentionCopyFormatting( () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "Test" ).build() ) ) );
|
||||
}
|
||||
|
||||
private static void testFormatRetentionCopyFormatting(Supplier<HoverEvent> hoverEventSupplier)
|
||||
{
|
||||
TextComponent first = new TextComponent( "Hello" );
|
||||
first.setBold( true );
|
||||
first.setColor( RED );
|
||||
first.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "test" ) );
|
||||
first.setHoverEvent( hoverEventSupplier.get() );
|
||||
|
||||
TextComponent second = new TextComponent( " world" );
|
||||
second.copyFormatting( first, ComponentBuilder.FormatRetention.ALL, true );
|
||||
assertEquals( first.isBold(), second.isBold() );
|
||||
assertEquals( first.getColor(), second.getColor() );
|
||||
assertEquals( first.getClickEvent(), second.getClickEvent() );
|
||||
assertEquals( first.getHoverEvent(), second.getHoverEvent() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderCloneCreate()
|
||||
{
|
||||
testBuilderClone( (builder) -> BaseComponent.toLegacyText( builder.create() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderCloneBuild()
|
||||
{
|
||||
testBuilderClone( (builder) -> BaseComponent.toLegacyText( builder.build() ) );
|
||||
}
|
||||
|
||||
private static void testBuilderClone(Function<ComponentBuilder, String> legacyTextFunction)
|
||||
{
|
||||
ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( RED ).append( "world" ).color( DARK_RED );
|
||||
ComponentBuilder cloned = new ComponentBuilder( builder );
|
||||
|
||||
assertEquals( legacyTextFunction.apply( builder ), legacyTextFunction.apply( cloned ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderAppendCreateMixedComponents()
|
||||
{
|
||||
testBuilderAppendMixedComponents(
|
||||
ComponentBuilder::create,
|
||||
(components, index) -> components[index]
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderAppendBuildMixedComponents()
|
||||
{
|
||||
testBuilderAppendMixedComponents(
|
||||
ComponentBuilder::build,
|
||||
(component, index) -> component.getExtra().get( index )
|
||||
);
|
||||
}
|
||||
|
||||
private static <T> void testBuilderAppendMixedComponents(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter)
|
||||
{
|
||||
ComponentBuilder builder = new ComponentBuilder( "Hello " );
|
||||
TextComponent textComponent = new TextComponent( "world " );
|
||||
TranslatableComponent translatableComponent = new TranslatableComponent( "item.swordGold.name" );
|
||||
// array based BaseComponent append
|
||||
builder.append( new BaseComponent[]
|
||||
{
|
||||
textComponent,
|
||||
translatableComponent
|
||||
} );
|
||||
ScoreComponent scoreComponent = new ScoreComponent( "myscore", "myobjective" );
|
||||
builder.append( scoreComponent ); // non array based BaseComponent append
|
||||
T component = componentBuilder.apply( builder );
|
||||
assertEquals( "Hello ", extraGetter.apply( component, 0 ).toPlainText() );
|
||||
assertEquals( textComponent.toPlainText(), extraGetter.apply( component, 1 ).toPlainText() );
|
||||
assertEquals( translatableComponent.toPlainText(), extraGetter.apply( component, 2 ).toPlainText() );
|
||||
assertEquals( scoreComponent.toPlainText(), extraGetter.apply( component, 3 ).toPlainText() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScore()
|
||||
{
|
||||
BaseComponent[] component = ComponentSerializer.parse( "{\"score\":{\"name\":\"@p\",\"objective\":\"TEST\",\"value\":\"hello\"}}" );
|
||||
String text = ComponentSerializer.toString( component );
|
||||
BaseComponent[] reparsed = ComponentSerializer.parse( text );
|
||||
|
||||
assertArrayEquals( component, reparsed );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStyle()
|
||||
{
|
||||
ComponentStyle style = ComponentSerializer.deserializeStyle( "{\"color\":\"red\",\"font\":\"minecraft:example\",\"bold\":true,\"italic\":false,\"obfuscated\":true}" );
|
||||
String text = ComponentSerializer.toString( style );
|
||||
ComponentStyle reparsed = ComponentSerializer.deserializeStyle( text );
|
||||
|
||||
assertEquals( style, reparsed );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderAppendCreate()
|
||||
{
|
||||
testBuilderAppend(
|
||||
() -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Hello world" ).create() ),
|
||||
ComponentBuilder::create,
|
||||
(components, index) -> components[index],
|
||||
BaseComponent::toPlainText,
|
||||
YELLOW + "Hello " + GREEN + "world!",
|
||||
BaseComponent::toLegacyText
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderAppendBuild()
|
||||
{
|
||||
testBuilderAppend(
|
||||
() -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "Hello world" ).build() ) ),
|
||||
ComponentBuilder::build,
|
||||
(component, index) -> component.getExtra().get( index ),
|
||||
(component) -> BaseComponent.toPlainText( component ),
|
||||
// An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component
|
||||
WHITE.toString() + YELLOW + "Hello " + GREEN + "world!",
|
||||
(component) -> BaseComponent.toLegacyText( component )
|
||||
);
|
||||
}
|
||||
|
||||
private static <T> void testBuilderAppend(Supplier<HoverEvent> hoverEventSupplier, Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter, Function<T, String> toPlainTextFunction, String expectedLegacyText, Function<T, String> toLegacyTextFunction)
|
||||
{
|
||||
ClickEvent clickEvent = new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/help " );
|
||||
HoverEvent hoverEvent = hoverEventSupplier.get();
|
||||
|
||||
ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( YELLOW );
|
||||
builder.append( new ComponentBuilder( "world!" ).color( GREEN ).event( hoverEvent ).event( clickEvent ).create() ); // Intentionally using create() to append multiple individual components
|
||||
|
||||
T component = componentBuilder.apply( builder );
|
||||
|
||||
assertEquals( extraGetter.apply( component, 1 ).getHoverEvent(), hoverEvent );
|
||||
assertEquals( extraGetter.apply( component, 1 ).getClickEvent(), clickEvent );
|
||||
assertEquals( "Hello world!", toPlainTextFunction.apply( component ) );
|
||||
assertEquals( expectedLegacyText, toLegacyTextFunction.apply( component ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderAppendLegacyCreate()
|
||||
{
|
||||
testBuilderAppendLegacy(
|
||||
ComponentBuilder::create,
|
||||
BaseComponent::toPlainText,
|
||||
YELLOW + "Hello " + GREEN + "world!",
|
||||
BaseComponent::toLegacyText
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderAppendLegacyBuild()
|
||||
{
|
||||
testBuilderAppendLegacy(
|
||||
ComponentBuilder::build,
|
||||
(component) -> BaseComponent.toPlainText( component ),
|
||||
// An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component
|
||||
WHITE.toString() + YELLOW + "Hello " + GREEN + "world!",
|
||||
(component) -> BaseComponent.toLegacyText( component )
|
||||
);
|
||||
}
|
||||
|
||||
private static <T> void testBuilderAppendLegacy(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction)
|
||||
{
|
||||
ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( YELLOW );
|
||||
builder.appendLegacy( GREEN + "world!" );
|
||||
|
||||
T component = componentBuilder.apply( builder );
|
||||
|
||||
assertEquals( "Hello world!", toPlainTextFunction.apply( component ) );
|
||||
assertEquals( expectedLegacyString, toLegacyTextFunction.apply( component ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicComponent()
|
||||
{
|
||||
TextComponent textComponent = new TextComponent( "Hello world" );
|
||||
textComponent.setColor( RED );
|
||||
|
||||
assertEquals( "Hello world", textComponent.toPlainText() );
|
||||
assertEquals( RED + "Hello world", textComponent.toLegacyText() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLegacyConverter()
|
||||
{
|
||||
BaseComponent[] test1 = TextComponent.fromLegacyText( AQUA + "Aqua " + RED + BOLD + "RedBold" );
|
||||
|
||||
assertEquals( "Aqua RedBold", BaseComponent.toPlainText( test1 ) );
|
||||
assertEquals( AQUA + "Aqua " + RED + BOLD + "RedBold", BaseComponent.toLegacyText( test1 ) );
|
||||
|
||||
BaseComponent[] test2 = TextComponent.fromLegacyText( "Text http://spigotmc.org " + GREEN + "google.com/test" );
|
||||
|
||||
assertEquals( "Text http://spigotmc.org google.com/test", BaseComponent.toPlainText( test2 ) );
|
||||
//The extra ChatColor instances are sometimes inserted when not needed but it doesn't change the result
|
||||
assertEquals( WHITE + "Text " + WHITE + "http://spigotmc.org" + WHITE
|
||||
+ " " + GREEN + "google.com/test" + GREEN, BaseComponent.toLegacyText( test2 ) );
|
||||
|
||||
ClickEvent url1 = test2[1].getClickEvent();
|
||||
assertNotNull( url1 );
|
||||
assertTrue( url1.getAction() == ClickEvent.Action.OPEN_URL );
|
||||
assertEquals( "http://spigotmc.org", url1.getValue() );
|
||||
|
||||
ClickEvent url2 = test2[3].getClickEvent();
|
||||
assertNotNull( url2 );
|
||||
assertTrue( url2.getAction() == ClickEvent.Action.OPEN_URL );
|
||||
assertEquals( "http://google.com/test", url2.getValue() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderCreate()
|
||||
{
|
||||
testBuilder(
|
||||
ComponentBuilder::create,
|
||||
BaseComponent::toPlainText,
|
||||
RED + "Hello " + BLUE + BOLD + "World" + YELLOW + BOLD + "!",
|
||||
BaseComponent::toLegacyText
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderBuild()
|
||||
{
|
||||
testBuilder(
|
||||
ComponentBuilder::build,
|
||||
(component) -> BaseComponent.toPlainText( component ),
|
||||
// An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component
|
||||
WHITE.toString() + RED + "Hello " + BLUE + BOLD + "World" + YELLOW + BOLD + "!",
|
||||
(component) -> BaseComponent.toLegacyText( component )
|
||||
);
|
||||
}
|
||||
|
||||
private static <T> void testBuilder(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction)
|
||||
{
|
||||
T component = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ).
|
||||
append( "World" ).bold( true ).color( BLUE ).
|
||||
append( "!" ).color( YELLOW ) );
|
||||
|
||||
assertEquals( "Hello World!", toPlainTextFunction.apply( component ) );
|
||||
assertEquals( expectedLegacyString, toLegacyTextFunction.apply( component ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderCreateReset()
|
||||
{
|
||||
testBuilderReset(
|
||||
ComponentBuilder::create,
|
||||
(components, index) -> components[index]
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderBuildReset()
|
||||
{
|
||||
testBuilderReset(
|
||||
ComponentBuilder::build,
|
||||
(component, index) -> component.getExtra().get( index )
|
||||
);
|
||||
}
|
||||
|
||||
private static <T> void testBuilderReset(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter)
|
||||
{
|
||||
T component = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED )
|
||||
.append( "World" ).reset() );
|
||||
|
||||
assertEquals( RED, extraGetter.apply( component, 0 ).getColor() );
|
||||
assertEquals( WHITE, extraGetter.apply( component, 1 ).getColor() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderCreateFormatRetention()
|
||||
{
|
||||
testBuilderFormatRetention(
|
||||
ComponentBuilder::create,
|
||||
(components, index) -> components[index]
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderBuildFormatRetention()
|
||||
{
|
||||
testBuilderFormatRetention(
|
||||
ComponentBuilder::build,
|
||||
(component, index) -> component.getExtra().get( index )
|
||||
);
|
||||
}
|
||||
|
||||
private static <T> void testBuilderFormatRetention(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter)
|
||||
{
|
||||
T noneRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED )
|
||||
.append( "World", ComponentBuilder.FormatRetention.NONE ) );
|
||||
|
||||
assertEquals( RED, extraGetter.apply( noneRetention, 0 ).getColor() );
|
||||
assertEquals( WHITE, extraGetter.apply( noneRetention, 1 ).getColor() );
|
||||
|
||||
HoverEvent testEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "test" ).build() ) );
|
||||
|
||||
T formattingRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED )
|
||||
.event( testEvent ).append( "World", ComponentBuilder.FormatRetention.FORMATTING ) );
|
||||
|
||||
assertEquals( RED, extraGetter.apply( formattingRetention, 0 ).getColor() );
|
||||
assertEquals( testEvent, extraGetter.apply( formattingRetention, 0 ).getHoverEvent() );
|
||||
assertEquals( RED, extraGetter.apply( formattingRetention, 1 ).getColor() );
|
||||
assertNull( extraGetter.apply( formattingRetention, 1 ).getHoverEvent() );
|
||||
|
||||
ClickEvent testClickEvent = new ClickEvent( ClickEvent.Action.OPEN_URL, "http://www.example.com" );
|
||||
|
||||
T eventRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED )
|
||||
.event( testEvent ).event( testClickEvent ).append( "World", ComponentBuilder.FormatRetention.EVENTS ) );
|
||||
|
||||
assertEquals( RED, extraGetter.apply( eventRetention, 0 ).getColor() );
|
||||
assertEquals( testEvent, extraGetter.apply( eventRetention, 0 ).getHoverEvent() );
|
||||
assertEquals( testClickEvent, extraGetter.apply( eventRetention, 0 ).getClickEvent() );
|
||||
assertEquals( WHITE, extraGetter.apply( eventRetention, 1 ).getColor() );
|
||||
assertEquals( testEvent, extraGetter.apply( eventRetention, 1 ).getHoverEvent() );
|
||||
assertEquals( testClickEvent, extraGetter.apply( eventRetention, 1 ).getClickEvent() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoopSimple()
|
||||
{
|
||||
TextComponent component = new TextComponent( "Testing" );
|
||||
component.addExtra( component );
|
||||
assertThrows( IllegalArgumentException.class, () -> ComponentSerializer.toString( component ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoopComplex()
|
||||
{
|
||||
TextComponent a = new TextComponent( "A" );
|
||||
TextComponent b = new TextComponent( "B" );
|
||||
b.setColor( AQUA );
|
||||
TextComponent c = new TextComponent( "C" );
|
||||
c.setColor( RED );
|
||||
a.addExtra( b );
|
||||
b.addExtra( c );
|
||||
c.addExtra( a );
|
||||
assertThrows( IllegalArgumentException.class, () -> ComponentSerializer.toString( a ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRepeated()
|
||||
{
|
||||
TextComponent a = new TextComponent( "A" );
|
||||
TextComponent b = new TextComponent( "B" );
|
||||
b.setColor( AQUA );
|
||||
a.addExtra( b );
|
||||
a.addExtra( b );
|
||||
ComponentSerializer.toString( a );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRepeatedError()
|
||||
{
|
||||
TextComponent a = new TextComponent( "A" );
|
||||
TextComponent b = new TextComponent( "B" );
|
||||
b.setColor( AQUA );
|
||||
TextComponent c = new TextComponent( "C" );
|
||||
c.setColor( RED );
|
||||
a.addExtra( b );
|
||||
a.addExtra( c );
|
||||
c.addExtra( a );
|
||||
a.addExtra( b );
|
||||
assertThrows( IllegalArgumentException.class, () -> ComponentSerializer.toString( a ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidColorCodes()
|
||||
{
|
||||
StringBuilder allInvalidColorCodes = new StringBuilder();
|
||||
|
||||
// collect all invalid color codes (e.g. §z, §g, ...)
|
||||
for ( char alphChar : "0123456789abcdefghijklmnopqrstuvwxyz".toCharArray() )
|
||||
{
|
||||
if ( ALL_CODES.indexOf( alphChar ) == -1 )
|
||||
{
|
||||
allInvalidColorCodes.append( COLOR_CHAR );
|
||||
allInvalidColorCodes.append( alphChar );
|
||||
}
|
||||
}
|
||||
|
||||
// last char is a single '§'
|
||||
allInvalidColorCodes.append( COLOR_CHAR );
|
||||
|
||||
String invalidColorCodesLegacyText = fromAndToLegacyText( allInvalidColorCodes.toString() );
|
||||
String emptyLegacyText = fromAndToLegacyText( "" );
|
||||
|
||||
// all invalid color codes and the trailing '§' should be ignored
|
||||
assertEquals( emptyLegacyText, invalidColorCodesLegacyText );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormattingOnlyTextConversion()
|
||||
{
|
||||
String text = "" + GREEN;
|
||||
|
||||
BaseComponent[] converted = TextComponent.fromLegacyText( text );
|
||||
assertEquals( GREEN, converted[0].getColor() );
|
||||
|
||||
String roundtripLegacyText = BaseComponent.toLegacyText( converted );
|
||||
|
||||
// color code should not be lost during conversion
|
||||
assertEquals( text, roundtripLegacyText );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals()
|
||||
{
|
||||
TextComponent first = new TextComponent( "Hello, " );
|
||||
first.addExtra( new TextComponent( "World!" ) );
|
||||
|
||||
TextComponent second = new TextComponent( "Hello, " );
|
||||
second.addExtra( new TextComponent( "World!" ) );
|
||||
|
||||
assertEquals( first, second );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals()
|
||||
{
|
||||
TextComponent first = new TextComponent( "Hello, " );
|
||||
first.addExtra( new TextComponent( "World." ) );
|
||||
|
||||
TextComponent second = new TextComponent( "Hello, " );
|
||||
second.addExtra( new TextComponent( "World!" ) );
|
||||
|
||||
assertNotEquals( first, second );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLegacyHack()
|
||||
{
|
||||
BaseComponent[] hexColored = new ComponentBuilder().color( of( Color.GRAY ) ).append( "Test" ).create();
|
||||
String legacy = BaseComponent.toLegacyText( hexColored );
|
||||
|
||||
BaseComponent[] reColored = TextComponent.fromLegacyText( legacy );
|
||||
|
||||
assertArrayEquals( hexColored, reColored );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLegacyResetInBuilderCreate()
|
||||
{
|
||||
testLegacyResetInBuilder(
|
||||
ComponentBuilder::create,
|
||||
ComponentSerializer::toString
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLegacyResetInBuilderBuild()
|
||||
{
|
||||
testLegacyResetInBuilder(
|
||||
ComponentBuilder::build,
|
||||
ComponentSerializer::toString
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasFormatting()
|
||||
{
|
||||
BaseComponent component = new TextComponent();
|
||||
assertFalse( component.hasFormatting() );
|
||||
|
||||
component.setBold( true );
|
||||
assertTrue( component.hasFormatting() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStyleIsEmpty()
|
||||
{
|
||||
ComponentStyle style = ComponentStyle.builder().build();
|
||||
assertTrue( style.isEmpty() );
|
||||
|
||||
style = ComponentStyle.builder()
|
||||
.bold( true )
|
||||
.build();
|
||||
assertFalse( style.isEmpty() );
|
||||
}
|
||||
|
||||
/*
|
||||
* In legacy chat, colors and reset both reset all formatting.
|
||||
* Make sure it works in combination with ComponentBuilder.
|
||||
*/
|
||||
private static <T> void testLegacyResetInBuilder(Function<ComponentBuilder, T> componentBuilder, Function<T, String> componentSerializer)
|
||||
{
|
||||
ComponentBuilder builder = new ComponentBuilder();
|
||||
BaseComponent[] a = TextComponent.fromLegacyText( "" + DARK_RED + UNDERLINE + "44444" + RESET + "dd" + GOLD + BOLD + "6666" );
|
||||
|
||||
String expected = "{\"extra\":[{\"underlined\":true,\"color\":\"dark_red\",\"text\":\"44444\"},{\"color\":"
|
||||
+ "\"white\",\"text\":\"dd\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"6666\"}],\"text\":\"\"}";
|
||||
assertEquals( expected, ComponentSerializer.toString( a ) );
|
||||
|
||||
builder.append( a );
|
||||
|
||||
String test1 = componentSerializer.apply( componentBuilder.apply( builder ) );
|
||||
assertEquals( expected, test1 );
|
||||
|
||||
BaseComponent[] b = TextComponent.fromLegacyText( RESET + "rrrr" );
|
||||
builder.append( b );
|
||||
|
||||
String test2 = componentSerializer.apply( componentBuilder.apply( builder ) );
|
||||
assertEquals(
|
||||
"{\"extra\":[{\"underlined\":true,\"color\":\"dark_red\",\"text\":\"44444\"},"
|
||||
+ "{\"color\":\"white\",\"text\":\"dd\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"6666\"},"
|
||||
+ "{\"color\":\"white\",\"text\":\"rrrr\"}],\"text\":\"\"}",
|
||||
test2 );
|
||||
}
|
||||
|
||||
private static String fromAndToLegacyText(String legacyText)
|
||||
{
|
||||
return BaseComponent.toLegacyText( TextComponent.fromLegacyText( legacyText ) );
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package net.md_5.bungee.api.chat;
|
||||
|
||||
import static net.md_5.bungee.api.ChatColor.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class TranslatableComponentTest
|
||||
{
|
||||
|
||||
@Test
|
||||
public void testMissingPlaceholdersAdded()
|
||||
{
|
||||
TranslatableComponent testComponent = new TranslatableComponent( "Test string with %s placeholders: %s", 2, "aoeu" );
|
||||
assertEquals( "Test string with 2 placeholders: aoeu", testComponent.toPlainText() );
|
||||
assertEquals( WHITE + "Test string with " + WHITE + "2" + WHITE + " placeholders: " + WHITE + "aoeu", testComponent.toLegacyText() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonSerialisation()
|
||||
{
|
||||
TranslatableComponent testComponent = new TranslatableComponent( "Test string with %s placeholder", "a" );
|
||||
String jsonString = ComponentSerializer.toString( testComponent );
|
||||
BaseComponent[] baseComponents = ComponentSerializer.parse( jsonString );
|
||||
|
||||
assertEquals( "Test string with a placeholder", BaseComponent.toPlainText( baseComponents ) );
|
||||
assertEquals( WHITE + "Test string with " + WHITE + "a" + WHITE + " placeholder", BaseComponent.toLegacyText( baseComponents ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTranslateComponent()
|
||||
{
|
||||
TranslatableComponent item = new TranslatableComponent( "item.swordGold.name" );
|
||||
item.setColor( AQUA );
|
||||
TranslatableComponent translatableComponent = new TranslatableComponent( "commands.give.success",
|
||||
item, "5", "thinkofdeath" );
|
||||
|
||||
assertEquals( "Given Golden Sword * 5 to thinkofdeath", translatableComponent.toPlainText() );
|
||||
assertEquals( WHITE + "Given " + AQUA + "Golden Sword" + WHITE + " * " + WHITE + "5" + WHITE + " to " + WHITE + "thinkofdeath",
|
||||
translatableComponent.toLegacyText() );
|
||||
|
||||
TranslatableComponent positional = new TranslatableComponent( "book.pageIndicator", "5", "50" );
|
||||
|
||||
assertEquals( "Page 5 of 50", positional.toPlainText() );
|
||||
assertEquals( WHITE + "Page " + WHITE + "5" + WHITE + " of " + WHITE + "50", positional.toLegacyText() );
|
||||
|
||||
TranslatableComponent one_four_two = new TranslatableComponent( "filled_map.buried_treasure" );
|
||||
assertEquals( "Buried Treasure Map", one_four_two.toPlainText() );
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package net.md_5.bungee.dialog;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||
import net.md_5.bungee.api.dialog.Dialog;
|
||||
import net.md_5.bungee.api.dialog.DialogBase;
|
||||
import net.md_5.bungee.api.dialog.NoticeDialog;
|
||||
import net.md_5.bungee.chat.VersionedComponentSerializer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SimpleTest
|
||||
{
|
||||
|
||||
@Test
|
||||
public void testNotice()
|
||||
{
|
||||
String json = "{type:\"minecraft:notice\",title:\"Hello\"}";
|
||||
Dialog deserialized = VersionedComponentSerializer.getDefault().getDialogSerializer().deserialize( json );
|
||||
System.err.println( deserialized );
|
||||
|
||||
Dialog notice = new NoticeDialog( new DialogBase( new ComponentBuilder( "Hello" ).color( ChatColor.RED ).build() ) );
|
||||
String newJson = VersionedComponentSerializer.getDefault().getDialogSerializer().toString( notice );
|
||||
System.err.println( newJson );
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user